2012-05-23 2 views
42

У меня есть следующие EF класса, полученных из базы данных (упрощенный)Как использовать LINQ Distinct() с несколькими полями

class Product 
{ 
    public string ProductId; 
    public string ProductName; 
    public string CategoryId; 
    public string CategoryName; 
} 

ProductId является Первичный ключ таблицы.

Для плохого дизайнерского решения, сделанного дизайнером БД (я не могу его изменить), в этой таблице есть CategoryId и CategoryName.

Мне нужен DropDownList с (отчетливый) CategoryId как Value и CategoryName в Текст. Поэтому я применил следующий код:

product.Select(m => new {m.CategoryId, m.CategoryName}).Distinct(); 

логически она должна создать анонимный объект с CategoryId и CategoryName как свойства. Distinct() гарантирует отсутствие пары дубликатов (CategoryId, CategoryName).

Но на самом деле это не работает. Насколько я понял, Distinct() работает только тогда, когда в коллекции есть только одно поле, иначе оно просто игнорирует их ... это правильно? Есть ли обходной путь? Благодаря!

UPDATE

К сожалению product является:

List<Product> product = new List<Product>(); 

Я нашел альтернативный способ получить тот же результат, как Distinct():

product.GroupBy(d => new {d.CategoryId, d.CategoryName}) 
     .Select(m => new {m.Key.CategoryId, m.Key.CategoryName}) 
+0

'есть только одно поле в коллекции' бессмысленна. Что вы имеете в виду? – leppie

+0

@leppie моя догадка есть, он имеет в виду при проецировании на одно значение, а не анонимный тип (содержащий более одного поля). – sehe

+0

«За плохое дизайнерское решение, сделанное разработчиком БД (я не могу его изменить)». Возможно, вы не можете изменить базу данных, но это не значит, что вы не можете исправить это в своей EF-модели. Это избили EF. – Steven

ответ

11

отчетливое() гарантирует, что нет пары дубликатов (CategoryId, CategoryN AME).

- точно, что

Анонимные типы 'волшебно' реализовать Equals и GetHashcode

Я предполагаю другую ошибку где-то. Чувствительность? Переменные классы? Не сопоставимые поля?

+0

То, что я, хотя, но ответ ниже говорит «нет»./меня смутило. – leppie

+0

@ Ieppie http://stackoverflow.com/questions/543482/linq-select-distinct-with-anonymous-types –

+0

@ RaphaëlAlthaus: Я тоже это подтвердил вручную :) – leppie

33

Я предполагаю, что вы используете различный метод, как вызов метода в списке. Вы должны использовать результат запроса как источник данных для своего DropDownList, например, материализуя его через ToList.

var distinctCategories = product 
         .Select(m => new {m.CategoryId, m.CategoryName}) 
         .Distinct() 
         .ToList(); 
DropDownList1.DataSource  = distinctCategories; 
DropDownList1.DataTextField = "CategoryName"; 
DropDownList1.DataValueField = "CategoryId"; 
+9

Да, я использую это, он не работает в мой случай ... – CiccioMiami

+1

@CiccioMiami upvoters, убедитесь, что вы применяете этот ответ только к анонимным типам. В типизированных классах может потребоваться такой делегат, как [этот] (http://stackoverflow.com/a/43010363/538763). – crokusek

+0

Правильно, отличительный будет работать только с анонимным типом – imdadhusen

4

Distinct метод возвращает отдельные элементы из последовательности.

Если вы посмотрите на его реализацию с Reflector, вы увидите, что он создает DistinctIterator для вашего анонимного типа. Отдельный итератор добавляет элементы к Set при перечислении по коллекции. Этот перечислитель пропускает все элементы, которые уже находятся в Set.Set использует методы GetHashCode и Equals для определения того, существует ли элемент в Set.

Как GetHashCode и Equals для анонимного типа? Как указано на msdn:

Equals и методы GetHashCode анонимных типов определены в терминах из Равных и методов GetHashCode свойств, два экземпляра одного и того же анонимного типа равны только тогда, когда все их свойства единый.

Таким образом, вы определенно должны иметь разные анонимные объекты при повторении в отдельной коллекции. И результат не зависит от того, сколько полей вы используете для анонимного типа.

2

Используйте ключевое слово Key в вашем выборе, как показано ниже.

product.Select(m => new {Key m.CategoryId, Key m.CategoryName}).Distinct();

Я понимаю, что это приносит старую нить, но полагал, что это может помочь некоторым людям. Обычно я кодирую в VB.NET при работе с .NET, поэтому Key может переводить по-другому на C#.

+0

Для выполнения отдельных полей в VB.Net нужно использовать ключевые слова «Ключ» для каждого свойства в анонимном типе. (В противном случае хэш-код, используемый для сравнения, не будет правильно рассчитан). В C# ключевое слово «Ключ» отсутствует, вместо этого все свойства в анонимных типах автоматически являются полями «Ключ». – voon

-2
Employee emp1 = new Employee() { ID = 1, Name = "Narendra1", Salary = 11111, Experience = 3, Age = 30 };Employee emp2 = new Employee() { ID = 2, Name = "Narendra2", Salary = 21111, Experience = 10, Age = 38 }; 
Employee emp3 = new Employee() { ID = 3, Name = "Narendra3", Salary = 31111, Experience = 4, Age = 33 }; 
Employee emp4 = new Employee() { ID = 3, Name = "Narendra4", Salary = 41111, Experience = 7, Age = 33 }; 

List<Employee> lstEmployee = new List<Employee>(); 

lstEmployee.Add(emp1); 
lstEmployee.Add(emp2); 
lstEmployee.Add(emp3); 
lstEmployee.Add(emp4); 

var eemmppss=lstEmployee.Select(cc=>new {cc.ID,cc.Age}).Distinct(); 
+3

Что этот ответ добавляет к уже полученным ответам? –

+0

Что делать, если я хочу сейчас сделать заказ по возрасту? – Si8

0

Отвечая на заголовок вопроса (то, что привлекло сюда людей) и не обращая внимания, что пример использовал анонимные типы ....

Это решение также будет работать без анонимных типов. Это не должно быть необходимо для анонимных типов.

класс Helper:

/// <summary> 
/// Allow IEqualityComparer to be configured within a lambda expression. 
/// From https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer 
/// </summary> 
/// <typeparam name="T"></typeparam> 
public class LambdaEqualityComparer<T> : IEqualityComparer<T> 
{ 
    readonly Func<T, T, bool> _comparer; 
    readonly Func<T, int> _hash; 

    /// <summary> 
    /// Simplest constructor, provide a conversion to string for type T to use as a comparison key (GetHashCode() and Equals(). 
    /// https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer, user "orip" 
    /// </summary> 
    /// <param name="toString"></param> 
    public LambdaEqualityComparer(Func<T, string> toString) 
     : this((t1, t2) => toString(t1) == toString(t2), t => toString(t).GetHashCode()) 
    { 
    } 

    /// <summary> 
    /// Constructor. Assumes T.GetHashCode() is accurate. 
    /// </summary> 
    /// <param name="comparer"></param> 
    public LambdaEqualityComparer(Func<T, T, bool> comparer) 
     : this(comparer, t => t.GetHashCode()) 
    { 
    } 

    /// <summary> 
    /// Constructor, provide a equality comparer and a hash. 
    /// </summary> 
    /// <param name="comparer"></param> 
    /// <param name="hash"></param> 
    public LambdaEqualityComparer(Func<T, T, bool> comparer, Func<T, int> hash) 
    { 
     _comparer = comparer; 
     _hash = hash; 
    } 

    public bool Equals(T x, T y) 
    { 
     return _comparer(x, y); 
    } 

    public int GetHashCode(T obj) 
    { 
     return _hash(obj); 
    }  
} 

Простейшее использование:

List<Product> products = duplicatedProducts.Distinct(
    new LambdaEqualityComparer<Product>(p => 
     String.Format("{0}{1}{2}{3}", 
      p.ProductId, 
      p.ProductName, 
      p.CategoryId, 
      p.CategoryName)) 
     ).ToList(); 

Самый простой (но не так эффективно) использование является отображение в строковое представление, так что пользовательские хеширования избежать. Равные строки уже имеют одинаковые хэш-коды.

Ссылка:
Wrap a delegate in an IEqualityComparer

0
public List<ItemCustom2> GetBrandListByCat(int id) 
    { 

     var OBJ = (from a in db.Items 
        join b in db.Brands on a.BrandId equals b.Id into abc1 
        where (a.ItemCategoryId == id) 
        from b in abc1.DefaultIfEmpty() 
        select new 
        { 
         ItemCategoryId = a.ItemCategoryId, 
         Brand_Name = b.Name, 
         Brand_Id = b.Id, 
         Brand_Pic = b.Pic, 

        }).Distinct(); 


     List<ItemCustom2> ob = new List<ItemCustom2>(); 
     foreach (var item in OBJ) 
     { 
      ItemCustom2 abc = new ItemCustom2(); 
      abc.CategoryId = item.ItemCategoryId; 
      abc.BrandId = item.Brand_Id; 
      abc.BrandName = item.Brand_Name; 
      abc.BrandPic = item.Brand_Pic; 
      ob.Add(abc); 
     } 
     return ob; 

    } 
Смежные вопросы