带可选参数的Linq/EF动态分组
本文关键字:EF 动态 Linq 参数 | 更新日期: 2023-09-27 18:25:37
我的搜索/组功能遇到问题。
上下文
我有一个客户对象列表(实体框架上下文),我想在这个列表中找到所有可能的重复项。如果一个对象是重复的,则条件应该是动态的。比方说可以通过UI进行选择。
型号
让我们假设以下部分是给定的。
我的客户类别
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、Mail、Birthday和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进行分组。有什么好的解决方案的建议吗?
Enumerable.GroupBy允许IEqualityComparer的参数。请参阅MSDN页面。
此比较器可能是查询的动态部分。您声明分组是基于选择标准的,因此这将给您:
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>
。
如何依靠围绕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 = "john.doe@test.com",
};
yield return new Customer
{
CustomerId = 2,
SearchName = "Jim",
CardNumber = "0000 0000 0000 0000 2",
Mail = "jim.doe@test.com",
};
yield return new Customer
{
CustomerId = 3,
SearchName = "Jack",
CardNumber = "0000 0000 0000 0000 3",
Mail = "jack.doe@test.com",
};
yield return new Customer
{
CustomerId = 4,
SearchName = "Jane",
CardNumber = "0000 0000 0000 0000 3",
Mail = "john.doe@test.com",
};
yield return new Customer
{
CustomerId = 4,
SearchName = "Joan",
CardNumber = "0000 0000 0000 0000 3",
Mail = "john.doe@test.com",
};
}
}
您的查询可以重写为:
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(...)
调用采用类型为Expression<Func<Customer, TKey>>
的表达式,其中TKey
是匿名类型。因此,如果您能够动态生成表达式,那么您就可以解决问题。
我必须承认,匿名类型是由编译器创建的,因此您的C#代码中还没有该类型。