it-swarm-pt.tech

Como implementar o IEqualityComparer para retornar valores distintos?

Eu tenho uma consulta L2E que retorna alguns dados que contêm objetos duplicados. Eu preciso remover esses objetos duplicados. Basicamente, devo assumir que, se seus IDs forem os mesmos, os objetos serão duplicados. Eu tentei q.Distinct(), mas isso ainda retornou objetos duplicados. Então tentei implementar meu próprio IEqualityComparer e passá-lo para o método Distinct(). O método falhou com o seguinte texto:

O LINQ to Entities não reconhece o método 'System.Linq.IQueryable1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable1 [DAL.MyDOClass], System.Collections.Generic.IEqualityComparer`1 [DAL.MyDOClass]) ', e esse método não pode ser convertido em uma expressão de armazenamento.

E aqui está a implementação do EqualityComparer:

  internal class MyDOClassComparer: EqualityComparer<MyDOClass>
    {
        public override bool Equals(MyDOClass x, MyDOClass y)
        {
            return x.Id == y.Id;
        }

        public override int GetHashCode(MyDOClass obj)
        {
            return obj == null ? 0 : obj.Id;
        }
    }

Então, como eu escrevo meus próprios IEqualityComparer corretamente?

43
Bogdan Verbenets

Um EqualityComparer não é o caminho a seguir - ele pode apenas filtrar o conjunto de resultados na memória, por exemplo:

var objects = yourResults.ToEnumerable().Distinct(yourEqualityComparer);

Você pode usar o método GroupBy para agrupar por IDs e o método First para permitir que seu banco de dados recupere apenas uma entrada exclusiva por ID, por exemplo:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());
116
Rich O'Kelly

rich.okelly e Ladislav Mrnka estão corretos de maneiras diferentes.

As duas respostas tratam do fato de que os métodos do IEqualityComparer<T> Não serão traduzidos para o SQL.

Acho que vale a pena examinar os prós e os contras de cada um, o que levará um pouco mais do que um comentário.

a abordagem de rich reescreve a consulta em uma consulta diferente com o mesmo resultado final. Seu código deve resultar em mais ou menos como você faria isso de forma eficiente com SQL codificado manualmente.

O Ladislav o extrai do banco de dados no ponto anterior ao distinto e, em seguida, uma abordagem na memória funcionará.

Como o banco de dados é ótimo para fazer o tipo de agrupamento e filtragem de rich's depende, provavelmente será o mais eficiente nesse caso. Você pode achar que a complexidade do que está acontecendo antes desse agrupamento é tal que o Linq para entidades não gera bem uma única consulta, mas produz várias consultas e, em seguida, faz parte do trabalho na memória, o que pode ser bem desagradável.

Geralmente, o agrupamento é mais caro do que o distinto nos casos na memória (especialmente se você o traz para a memória com AsList() em vez de AsEnumerable()). Portanto, se você já o trouxer para a memória neste estágio devido a algum outro requisito, seria mais eficiente.

Também seria a única opção se sua definição de igualdade não estivesse relacionada com o que está disponível apenas no banco de dados e, é claro, permite que você alterne as definições de igualdade se desejar fazê-lo com base em um IEqualityComparer<T> Passou como um parâmetro.

Ao todo, rich's é a resposta que eu diria que provavelmente seria a melhor escolha aqui, mas os diferentes prós e contras dos Ladislav's em comparação aos rich também valem a pena estudar e considerar.

16
Jon Hanna

Você não vai. O operador Distinct é chamado no banco de dados, portanto, qualquer código que você escreve no seu aplicativo não pode ser usado (você não pode mover sua lógica do comparador de igualdade para SQL), a menos que esteja satisfeito com o carregamento de todos os valores não distintos e faça uma filtragem distinta sua aplicação.

var query = (from x in context.EntitySet where ...).ToList()
                                                   .Distinct(yourComparer);
7
Ladislav Mrnka

Resposta tardia, mas você pode fazer melhor: se o objeto DAL é parcial (geralmente é se for um objeto de banco de dados), você pode estendê-lo da seguinte maneira:

public partial class MyDOClass :  IEquatable<MyDOClass>
    {

        public override int GetHashCode()
        {
            return Id == 0 ? 0 : Id;
        }

        public bool Equals(MyDOClass other)
        {
            return this.Id == other.Id;
        }
    }

E o distinto funcionará sem sobrecarga.

Caso contrário, você pode criar a classe IEqualityComparer assim:

internal class MyDOClassComparer : MyDOClass,  IEquatable<MyDOClass>, IEqualityComparer<MyDOClass>
    {
        public override int GetHashCode()
        {
            return Id == 0 ? 0 : Id;
        }

        public bool Equals(MyDOClass other)
        {
            return this.Id == other.Id;
        }

        public bool Equals(MyDOClass x, MyDOClass y)
        {
            return x.Id == y.Id;
        }

        public int GetHashCode(MyDOClass obj)
        {
            return Id == 0 ? 0 : Id;
        }
    }

E, novamente, use o Distinct sem sobrecarga

2
gil kr

GroupBy() pode ser uma solução melhor do que Distinct() - como mencionado na resposta mais votada on esta pergunta .

2
Martin Zaloga