2014-10-06 145 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]", 
     }; 
    } 
}