2010-12-14 17 views
9

Hasta hace poco, estaba usando un Distintivo en LINQ para seleccionar una categoría distinta (una enumeración) de una tabla. Esto estaba funcionando bien.Usando Distintivo con LINQ y Objetos

Ahora necesito distinguirlo de una clase que contenga una categoría y un país (ambas enumeraciones). The Distinct no está funcionando ahora.

¿Qué estoy haciendo mal?

Respuesta

13

Creo este post explica su problema: http://blog.jordanterrell.com/post/LINQ-Distinct()-does-not-work-as-expected.aspx

El contenido del enlace anterior se puede resumir diciendo que el método distinct() puede ser reemplazado por hacer lo siguiente.

var distinctItems = items 
     .GroupBy(x => x.PropertyToCompare) 
     .Select(x => x.First()); 
+0

Esto funcionó de maravilla. Gracias. –

+0

Por favor, no responda meramente vinculando. Incluido el código relevante en la respuesta. –

+1

Sí, ya no hago eso, pero en 2010 no presté atención a esta práctica. – Stilgar

4

probar un IQualityComparer

public class MyObjEqualityComparer : IEqualityComparer<MyObj> 
{ 
    public bool Equals(MyObj x, MyObj y) 
    { 
     return x.Category.Equals(y.Category) && 
       x.Country.Equals(y.Country); 
    } 

    public int GetHashCode(MyObj obj) 
    { 
     return obj.GetHashCode(); 
    } 
} 

a continuación, utilizar aquí

var comparer = new MyObjEqualityComparer(); 
myObjs.Where(m => m.SomeProperty == "whatever").Distinct(comparer); 
+0

Me gusta esta solución, sin embargo, tuve un problema con ella. GetHashCode hacía que no encontrara las coincidencias. Tuve que cambiarlo a algo como 'return obj.Category.GetHashCode() + obj.Country.GetHashCode()' –

3

Para la explicación, echar un vistazo a otras respuestas. Solo estoy brindando una forma de manejar este problema.

Es posible que como this:

public class LambdaComparer<T>:IEqualityComparer<T>{ 
    private readonly Func<T,T,bool> _comparer; 
    private readonly Func<T,int> _hash; 
    public LambdaComparer(Func<T,T,bool> comparer): 
    this(comparer,o=>0) {} 
    public LambdaComparer(Func<T,T,bool> comparer,Func<T,int> hash){ 
    if(comparer==null) throw new ArgumentNullException("comparer"); 
    if(hash==null) throw new ArgumentNullException("hash"); 
    _comparer=comparer; 
    _hash=hash; 
    } 
    public bool Equals(T x,T y){ 
    return _comparer(x,y); 
    } 
    public int GetHashCode(T obj){ 
    return _hash(obj); 
    } 
} 

Uso:

public void Foo{ 
    public string Fizz{get;set;} 
    public BarEnum Bar{get;set;} 
} 

public enum BarEnum {One,Two,Three} 

var lst=new List<Foo>(); 
lst.Distinct(new LambdaComparer<Foo>(
    (x1,x2)=>x1.Fizz==x2.Fizz&& 
      x1.Bar==x2.Bar)); 

Incluso puede envolver alrededor de evitar escribir ruidosa new LambdaComparer<T>(...) cosa:

public static class EnumerableExtensions{ 
public static IEnumerable<T> SmartDistinct<T> 
    (this IEnumerable<T> lst, Func<T, T, bool> pred){ 
    return lst.Distinct(new LambdaComparer<T>(pred)); 
} 
} 

Uso:

lst.SmartDistinct((x1,x2)=>x1.Fizz==x2.Fizz&&x1.Bar==x2.Bar); 

NB: funciona de forma fiable sólo para Linq2Objects

1

No estás haciendo mal, es sólo la mala ejecución de .Distinct() en .NET Framework.

Una forma de solucionarlo ya se muestra en las otras respuestas, pero también hay una solución más corta disponible, lo cual tiene la ventaja de que se puede usar como un método extensión fácilmente por todas partes sin tener que modificar el hash del objeto valores.

Tome un vistazo a esto:


Uso:

var myQuery=(from x in Customers select x).MyDistinct(d => d.CustomerID); 

Nota: este ejemplo se utiliza una consulta de base de datos, sino que también funciona con una lista de objetos enumerables.


Declaración de MyDistinct:

public static class Extensions 
{ 
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, 
                Func<T, V> f) 
    { 
     return query.GroupBy(f).Select(x=>x.First()); 
    } 
} 

Y funciona para todo, objetos, así como las entidades.Si es necesario, puede crear un segundo método de extensión sobrecargado para IQueryable<T> simplemente reemplazando el tipo de retorno y el primer tipo de parámetro en el ejemplo que he dado arriba.

Cuestiones relacionadas