2011-12-19 12 views
32

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. He intentado q.Distinct(), pero eso todavía devolvió objetos duplicados. Luego intenté implementar mi propio IEqualityComparer y pasarlo al método Distinct(). El método ha fallado con el siguiente texto:¿Cómo implementar IEqualityComparer para devolver valores distintos?

LINQ a Entidades 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 ]) ' método, y este método no se puede traducir a una expresión de tienda.

Y aquí es 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; 
     } 
    } 

Así que ¿Cómo escribo mi propia IEqualityComparer correctamente?

Respuesta

86

Un EqualityComparer no es el camino a seguir - que sólo puede filtrar su conjunto de resultados en la memoria por ejemplo:

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

Usted puede utilizar el método GroupBy al grupo de identificaciones y el método First para dejar sólo a su base de datos recuperar una entrada única por ID, por ejemplo:

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

1 Este es un protector de la vida, sin embargo, tenga en cuenta que no se puede utilizar. Primero() en su lugar, tendrá que usar .FirstOrDefault() –

+0

¡Le debo una educación! Una de esas respuestas desearía poder subir la voz: ¡voto! – seebiscuit

+0

@yoelhalb no garantiza GroupBy ninguna de las agrupaciones devueltas están vacías? No hay forma de que una de las agrupaciones devueltas esté vacía, ya que las agrupaciones se forman separando los elementos – vijrox

7

No lo hará. Se llama al operador Distinct 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 comparación de igualdad a SQL) a menos que esté satisfecho con cargar todos los valores no distintivos y realizar un filtrado distinto en su aplicación.

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

¿Por qué' ToList() 'en lugar de' ToEnumerable() '? –

+2

@Jon: tienes razón. 'ToEnumerable' sería suficiente. –

14

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

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

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

El enfoque de rich reescribe la consulta a 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's lo saca de la base de datos en el punto anterior al distinto, y luego funcionará un enfoque en memoria.

Dado que la base de datos es excelente para realizar el tipo de agrupación y filtrado de los que dependen los ricos, es probable que sea el que mejor se desempeñe 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 casos en memoria (especialmente si la trae a la memoria con AsList() en 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 era algo que no se relacionaba bien con lo que está disponible solo en la base de datos, y por supuesto le permite cambiar las definiciones de igualdad si así lo desea. un IEqualityComparer<T> pasado como parámetro.

En general, rich's es la respuesta que diría sería probablemente la mejor opción aquí, pero los diferentes pros y contras de Ladislav en comparación con los ricos hacen que valga la pena estudiarlos y considerarlos.

1

respuesta tardía pero se puede hacer mejor: si el objeto DAL es parcial (por lo general se si es 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 el distinto funcionará sin ninguna sobrecarga en él.

Si no es así, puede crear la clase IEqualityComparer así:

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, utilice el distinto sin ninguna sobrecarga

Cuestiones relacionadas