2014-10-06 129 views
1

我正在面對我的搜索/組功能的問題。Linq/EF Dynamic Group by with可選參數

語境

我有客戶對象(實體框架上下文)的列表,我想找到這個列表中的所有可能的重複。對象是重複的標準應該是動態的。比方說,用戶界面可以選擇。

型號

假設以下部分給出。

我CustomerClass

public class Customer 
{ 
    public int CustomerId { get; set; } 
    public string SearchName { get; set; } 
    public string Mail { get; set; } 
    public DateTime? Birthday { get; set; } 
    public string CardNumber { get; set; } 
    public DateTime Created { get; set; } 
} 

爲重複的可能標準是:SearchName,郵件,生日和CardNumber。

下面的函數返回一個適當的結果:

public IList<Customer> GetPossibleDuplicates() 
    { 
     IList<Customer> list; 

     list = 
      (from s in this.Context.Customers 
       group s by new 
          { 
           s.SearchName, 
           s.CardNumber 
          } 
       into g where g.Count() > 1 select g) 
       .SelectMany(g => g) 
       .OrderBy(o => o.SearchName) 
       .ThenBy(c => c.Created) 
       .ToList(); 


     return list; 
    } 

我有問題是由陳述「動態」,所以根據所選擇的creteria分組製成,使該組。任何建議以獲得良好解決方案?

回答

0

Enumerable.GroupBy允許IEqualityComparer的參數。見the MSDN page

這個比較器可能是查詢的動態部分。幽州分組是基於選擇標準所以這會給你:

public IList<Customer> GetPossibleDuplicates() 
{ 
    var comparer = SomeMethodReturningAnEqualityComparerBasedOnSelectionCriteria(); 
    return this.Context.Customers 
      .GroupBy(customer => customer, comparer) 
      .SelectMany(g => g) 
      .OrderBy(o => o.SearchName) 
      .ThenBy(c => c.Created) 
      .ToList(); 
} 

返回比較器的方法將返回IEqualityComparer<Customer>

+0

謝謝安迪!提示導致更好的解決方案。但是對於IEqualityComparer,我只能在內存集合中檢查equaltiy嗎?因此,與多個客戶的thousends似乎表現不佳,對吧? (this.Context是一個實體框架上下文) – zorkin82 2014-10-06 14:16:49

+0

你說得對。我的回答是針對內存集合,我沒有考慮過這是實體框架。當Entity Framework試圖將其轉換爲SQL語句時,您甚至可能會因此發生運行時錯誤。 – 2014-10-06 14:49:37

+0

好的。我將其標記爲已接受,因爲它符合我的要求。 – zorkin82 2014-10-07 12:33:32

-1

你的查詢可以被改寫爲:

var result = this.Context.Customers 
    .GroupBy(x => new { x.SearchName, x.CardNumber }) 
    .Where(x => x.Count() > 1) 
    .SelectMany(x => x) 
    .OrderBy(x => x.SearchName) 
    .ThenBy(x => x.Created) 
    .ToList(); 

你可以看到.GroupBy(...)調用需要在TKey是一個匿名類型Expression<Func<Customer, TKey>>類型的表達式。因此,如果您可以設法動態生成表達式,那麼您可以解決您的問題。

我必須承認,匿名類型是由編譯器創建的,因此您的C#代碼中沒有該類型。

0

如何依託各地IComparable

Func<Customer, IComparable> criteria 

和您的查詢封裝的方法建立了一個標準,應該是這樣的

public IList<Customer> FindByCriteria(Func<Customer, IComparable> criteria) 
{ 
    var query = from customer in FindAll() 
       group customer by criteria(customer) 
       into groups 
       where groups.Count() > 1 
       from item in groups 
       orderby item.SearchName, item.Created 
       select item; 

    return query.ToList(); 
} 

然後你就可以玩弄自己的數據結構,甚至與Tuple

void Main() 
{ 
    var repository = new Repository(); 

    //Find all 
    repository.FindAll().Dump(); 

    // Find by mail 
    var mail = new Func<Customer, IComparable>(customer => 
    { 
     return Tuple.Create(customer.Mail); 
    }); 
    repository 
     .FindByCriteria(mail) 
     .Dump(); 

    // Find by mail and card number 
    var multi = new Func<Customer, IComparable>(customer => 
    { 
     return Tuple.Create(customer.CardNumber, customer.Mail); 
    }); 
    repository 
     .FindByCriteria(multi) 
     .Dump(); 
} 

完整的存儲庫代碼如下所示

public class Repository 
{ 
    public IList<Customer> FindByCriteria(Func<Customer, IComparable> criteria) 
    { 
     var query = from customer in FindAll() 
        group customer by criteria(customer) 
        into groupings 
        where groupings.Count() > 1 
        from grouping in groupings 
        orderby grouping.SearchName, grouping.Created 
        select grouping; 

     return query.ToList(); 
    } 

    public IEnumerable<Customer> FindAll() 
    { 
     yield return new Customer 
     { 
      CustomerId = 1, 
      SearchName = "John", 
      CardNumber = "0000 0000 0000 0000 1", 
      Mail = "[email protected]", 
     }; 

     yield return new Customer 
     { 
      CustomerId = 2, 
      SearchName = "Jim", 
      CardNumber = "0000 0000 0000 0000 2", 
      Mail = "[email protected]", 
     }; 

     yield return new Customer 
     { 
      CustomerId = 3, 
      SearchName = "Jack", 
      CardNumber = "0000 0000 0000 0000 3", 
      Mail = "[email protected]", 
     }; 

     yield return new Customer 
     { 
      CustomerId = 4, 
      SearchName = "Jane", 
      CardNumber = "0000 0000 0000 0000 3", 
      Mail = "[email protected]", 
     }; 

     yield return new Customer 
     { 
      CustomerId = 4, 
      SearchName = "Joan", 
      CardNumber = "0000 0000 0000 0000 3", 
      Mail = "[email protected]", 
     }; 
    } 
}