c# entity-framework distinct iequalitycomparer

distinct linq c# lambda



¿Cómo implementar IEqualityComparer para devolver valores distintos? (5)

Tengo una consulta L2E que devuelve algunos datos que contienen objetos duplicados. Necesito eliminar esos objetos duplicados. Básicamente, debería suponer que si sus identificaciones son las mismas, entonces los objetos son duplicados. Intenté q.Distinct() , pero eso aún devolvió objetos duplicados. Luego intenté implementar mi propio IEqualityComparer y pasarlo al método Distinct() . El método falló con el siguiente texto:

LINQ to Entities no reconoce el método ''System.Linq.IQueryable 1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable 1 [DAL.MyDOClass], System.Collections.Generic.IEqualityComparer`1 [DAL.MyDOClass ]) '', y este método no se puede traducir a una expresión de tienda.

Y aquí está la implementación de 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; } }

Entonces, ¿cómo puedo escribir mi propio IEqualityComparer correctamente?


No lo harás. Distinct llama a un operador diferente en la base de datos para que no pueda usarse ningún código que escriba en su aplicación (no puede mover su lógica de comparador de igualdad a SQL) a menos que esté satisfecho con cargar todos los valores no distintivos y hacer un filtrado distinto en su aplicación.

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


Respuesta tardía, pero puede hacerlo mejor: si el objeto DAL es parcial (normalmente lo es si se trata de un objeto DB), puede extenderlo así:

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; } }

Y lo distinto funcionará sin ninguna sobrecarga en él.

Si no, puede crear la clase IEqualityComparer de esta manera:

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; } }

Y de nuevo, use Distinct sin ninguna sobrecarga


Un EqualityComparer no es el camino a seguir; solo puede filtrar el conjunto de resultados en la memoria, por ejemplo:

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

Puede usar el método GroupBy para agrupar por ID y el First método para permitir que su base de datos solo recupere una entrada única por ID, por ejemplo:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());


rich.okelly y Ladislav Mrnka son correctos de diferentes maneras.

Ambas respuestas se refieren al hecho de que los IEqualityComparer<T> no se traducirán a SQL.

Creo que vale la pena analizar los pros y los contras de cada uno, lo que requerirá un poco más que un comentario.

El enfoque de Rich vuelve a escribir la consulta en una consulta diferente con el mismo resultado final. Su código debería dar como resultado más o menos cómo lo haría de manera eficiente con SQL codificado a mano.

Ladislav lo saca de la base de datos en el punto anterior al distinto, y luego un enfoque en memoria funcionará.

Dado que la base de datos es excelente para hacer el tipo de agrupación y depende de los filtros ricos, es probable que sea el más eficiente en este caso. Sin embargo, podría descubrir que la complejidad de lo que sucede antes de esta agrupación es tal que Linq-to-entities no genera muy bien una sola consulta, sino que produce un montón de consultas y luego parte del trabajo en la memoria, que podría ser bastante desagradable.

En general, la agrupación es más costosa que distinta en los casos en memoria (especialmente si la trae a la memoria con AsList() lugar de AsEnumerable() ). Entonces, si alguno de ustedes ya lo iba a traer a la memoria en esta etapa debido a algún otro requisito, sería más eficiente.

También sería la única opción si su definición de igualdad fuera algo que no se relacionara bien con lo que está disponible solo en la base de datos, y por supuesto le permite cambiar las definiciones de igualdad si quisiera hacerlo en base a un IEqualityComparer<T> pasó como un parámetro.

En general, la respuesta de Rich''s es probablemente la más acertada, pero los diferentes pros y contras de Ladislav en comparación con los de los ricos hace que valga la pena estudiarlos y considerarlos.


GroupBy() puede ser una mejor solución que Distinct() , como se menciona en la respuesta mejor valorada en esta pregunta .