Uma aplicação nossa lê de um arquivo um monte de registros de produtos pra dentro de uma List<Produtos>. Aí eu fui jogar estes produtos na tabela de Produtos no BD, mas como havia registros repetidos no arquivo, apareceram vários registros para o mesmo produto no banco.
“Bem, é só fazer o DISTINCT na lista”. Ok. Fui olhar o método Distinct() do Linq e achei duas assinaturas: uma recebe uma expressão lambda cujo resultado, ao ser avaliado para cada registro da lista, define os registros distintos; e outra recebe uma instância de IEqualityComparer, a qual nem dei uma segunda olhada.
Pra gerar minha lista de produtos distintos, escrevi:
// 1ª tentativa: Distinct(p => p) pra retornar uma lista de produtos sem repetidos List<Produto> listaSemRepetidos = listaProdutos.Distinct(p => p);
Antes de ir escovar bit e partir pra uma implementação customizada de Equals() na minha classe Produtos, resolvi dar uma olhada com mais calma na segunda assinatura do Distinct(), aquela que recebe uma instância de IEqualityComparer. Esta interface contem dois métodos: bool Equals(<T>, <T>) e int GetHashCode(). “Morreu”, pensei eu. “É só implementar a interface e na implementação do Equals(<T>,<T>), comparar os campos das instâncias recebidas”. Minha implementação ficou assim:
public class ComparadorProdutos : IEqualityComparer<AcessoDados.Entidades.Produto> { public bool Equals(AcessoDados.Entidades.Produto p1, AcessoDados.Entidades.Produto p2) { // Trata os casos nos quais há objetos nulos if(p1 == null && p2 == null) return true; if(p1 == null && p2 != null) return false; if(p1 != null && p2 == null) return false; // Se nenhum dos dois é nulo, retorna true se os campos “importantes” contém o mesmo valor return ( p1.CodigoProduto == p2.CodigoProduto && p1.DataInicial == p2.DataInicial && p1.DataFinal == p2.DataFinal && p1.CodigoNCM == p2.CodigoNCM && p1.Descricao == p2.Descricao && p1.UnidadeMedida == p2.UnidadeMedida && p1.AliquotaICMS == p2.AliquotaICMS && p1.AliquotaIPI == p2.AliquotaIPI && p1.ReducaoBaseCalculoICMS == p2.ReducaoBaseCalculoICMS && p1.BaseCalculoICMSSubstTrib == p2.BaseCalculoICMSSubstTrib && p1.IdCliente == p2.IdCliente ); } // Retorna qualquer coisa em GetHashCode() pq não vou usar esse trem mesmo public int GetHashCode(AcessoDados.Entidades.Produto p) { if(p == null) return -1; else return p.GetHashCode(); } }Na implementação do GetHashCode() coloquei qualquer leseira, porque o que o Distinct() ia usar mesmo era o Equals(). Testei com um programinha console, tudo ok, agora é só chamar o Distinct() passando uma instância de ComparadorProdutos.
// 2ª tentativa: Passando uma implementação de IEqualityComparer para Distinct() List<Produto> listaSemRepetidos = listaProdutos.Distinct(new ComparadorProdutos());
public int GetHashCode(AcessoDados.Entidades.Produto p) { if(p == null) return 0; else return new { p.CodigoProduto, p.DataInicial, p.DataFinal, p.CodigoNCM, p.Descricao, p.UnidadeMedida, p.AliquotaICMS, p.AliquotaIPI, p.ReducaoBaseCalculoICMS, p.BaseCalculoICMSSubstTrib, p.IdCliente }.GetHashCode(); }Se duas instâncias da classe Produto tem os mesmos valores nestes campos, seus hash codes serão iguais. E aí meu Distinct(new ComparadorProdutos) finalmente funcionou.
Cris me ajudando na redação do post no Galeão
[]s,
GB