通过对象列表中的多个参数过滤查询
Filter query by multiple parameters from the list of objects
有一个 class 看起来像这样:
class CustomerPurchase {
int CustomerId;
int PurchaseId;
int Cost;
}
我使用代码优先方法和 Entity Framework 核心迁移来创建数据库。
还有一个看起来像这样的过滤器:
class Filter {
int CustomerId;
int PurchaseId;
}
现在,我正在尝试按 ID 对过滤数据。
这是我的代码:
var filters = new List<Filter>();
ctx.CustomerPurchases
.Where(p => filters.Any(f =>
f.CustomerId == p.CustomerId &&
f.PurchaseId == p.PurchaseId))
.ToList();
显然,这是行不通的。 filters
是对象列表,它不会被翻译成 SQL 查询。
我遇到了一个例外:The LINQ expression blah could not be translated.
那么,我该如何让它发挥作用呢?这个特定的 table 包含几百万条记录,所以我无法在客户端过滤它。
我与 entity framework 无关:请随时提出不同的方法,以防万一。
出于显而易见的原因,我不会生成原始查询,例如 WHERE (CustomerId = {1} AND PurchaseId = {2}) OR ...
。除此之外的任何东西都是受欢迎的。
我之前遇到过这个问题,这里是我如何解决它的
在这种情况下你有两个选择
1- 根据 ids 过滤
var res1 = dbcontext.CustomerPurchases
.Where(p => filters.Select(c=>c.PurchaseId).Contains(p.PurchaseId))
.Where(p => filters.Select(c => c.CustomerId).Contains(p.CustomerId));
2- 使用包含
var resq = await dbcontext.CustomerPurchases
.Where(p=> filters.Contains(new Filter { CustomerId = p.CustomerId,PurchaseId = p.PurchaseId }))
.ToListAsync();
但是如果你 运行 这个你不会得到任何结果,除非你实施 IEquatable
所以你的 Filter
class 看起来像这样
public class Filter : IEquatable<Filter>
{
public int CustomerId;
public int PurchaseId;
public bool Equals(Filter? other)
{
return this.PurchaseId == other.PurchaseId &&
this.CustomerId == other.CustomerId;
}
}
两种方式的完整代码如下
var options = new DbContextOptionsBuilder<ApplicationDBContext>()
.UseInMemoryDatabase("test")
.Options;
var dbcontext = new ApplicationDBContext(options);
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 1,PurchaseId = 1,Cost = 10 });
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 1, PurchaseId = 2, Cost = 10 });
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 1, PurchaseId = 3, Cost = 10 });
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 2, PurchaseId = 2, Cost = 10 });
await dbcontext.SaveChangesAsync();
var filters = new List<Filter>();
filters.Add(new Filter { CustomerId = 1, PurchaseId = 2 });
filters.Add(new Filter { CustomerId = 2, PurchaseId = 2 });
var resq = await dbcontext.CustomerPurchases
.Where(p=> filters.Contains(new Filter { CustomerId = p.CustomerId,PurchaseId = p.PurchaseId }))
.ToListAsync();
foreach (var item in resq)
{
Console.WriteLine($" CustomerId : {item.CustomerId} , PurchaseId : {item.PurchaseId} Cost : {item.Cost}");
}
var res1 = dbcontext.CustomerPurchases
.Where(p => filters.Select(c=>c.PurchaseId).Contains(p.PurchaseId))
.Where(p => filters.Select(c => c.CustomerId).Contains(p.CustomerId));
var res = await res1.ToListAsync();
Console.WriteLine("===========================================================");
foreach (var item in res)
{
Console.WriteLine($" CustomerId : {item.CustomerId} , PurchaseId : {item.PurchaseId} Cost : {item.Cost}");
}
和运行ning代码
更新
更改为 SQL 服务器后,我仍然遇到错误,因此当 运行 在 SQL 服务器
上运行时,选项 2 不是一个选项
但我找到了另一个解决方案,我可以根据我拥有的过滤器列表构建 where 子句
我找到这个 PredicateBuilder
这是使用 predicate builder
的代码
var whereclause = PredicateBuilder.False<CustomerPurchase>();
foreach (var filterrow in filters)
{
whereclause = whereclause.Or(c => c.CustomerId == filterrow.CustomerId && c.PurchaseId == filterrow.PurchaseId);
}
var resqq = dbcontext.CustomerPurchases.Where(whereclause);
var resq = await resqq.ToListAsync();
foreach (var item in resq)
{
Console.WriteLine($" CustomerId : {item.CustomerId} , PurchaseId : {item.PurchaseId} Cost : {item.Cost}");
}
这将构建查询,该查询将由 sql 转换为以下语句
DECLARE @__filterrow_CustomerId_0 int = 1;
DECLARE @__filterrow_PurchaseId_1 int = 2;
DECLARE @__filterrow_CustomerId_2 int = 2;
DECLARE @__filterrow_PurchaseId_3 int = 2;
SELECT [c].[PurchaseId], [c].[CustomerId]
FROM [dbo].[CustomerPurchase] AS [c]
WHERE (([c].[CustomerId] = @__filterrow_CustomerId_0) AND ([c].[PurchaseId] = @__filterrow_PurchaseId_1))
OR
(([c].[CustomerId] = @__filterrow_CustomerId_2) AND ([c].[PurchaseId] = @__filterrow_PurchaseId_3))
这是 PredicateBuyilder
的完整 class
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
希望能回答你的问题!!
有一个 class 看起来像这样:
class CustomerPurchase {
int CustomerId;
int PurchaseId;
int Cost;
}
我使用代码优先方法和 Entity Framework 核心迁移来创建数据库。
还有一个看起来像这样的过滤器:
class Filter {
int CustomerId;
int PurchaseId;
}
现在,我正在尝试按 ID 对过滤数据。
这是我的代码:
var filters = new List<Filter>();
ctx.CustomerPurchases
.Where(p => filters.Any(f =>
f.CustomerId == p.CustomerId &&
f.PurchaseId == p.PurchaseId))
.ToList();
显然,这是行不通的。 filters
是对象列表,它不会被翻译成 SQL 查询。
我遇到了一个例外:The LINQ expression blah could not be translated.
那么,我该如何让它发挥作用呢?这个特定的 table 包含几百万条记录,所以我无法在客户端过滤它。
我与 entity framework 无关:请随时提出不同的方法,以防万一。
出于显而易见的原因,我不会生成原始查询,例如 WHERE (CustomerId = {1} AND PurchaseId = {2}) OR ...
。除此之外的任何东西都是受欢迎的。
我之前遇到过这个问题,这里是我如何解决它的 在这种情况下你有两个选择
1- 根据 ids 过滤
var res1 = dbcontext.CustomerPurchases
.Where(p => filters.Select(c=>c.PurchaseId).Contains(p.PurchaseId))
.Where(p => filters.Select(c => c.CustomerId).Contains(p.CustomerId));
2- 使用包含
var resq = await dbcontext.CustomerPurchases
.Where(p=> filters.Contains(new Filter { CustomerId = p.CustomerId,PurchaseId = p.PurchaseId }))
.ToListAsync();
但是如果你 运行 这个你不会得到任何结果,除非你实施 IEquatable
所以你的 Filter
class 看起来像这样
public class Filter : IEquatable<Filter>
{
public int CustomerId;
public int PurchaseId;
public bool Equals(Filter? other)
{
return this.PurchaseId == other.PurchaseId &&
this.CustomerId == other.CustomerId;
}
}
两种方式的完整代码如下
var options = new DbContextOptionsBuilder<ApplicationDBContext>()
.UseInMemoryDatabase("test")
.Options;
var dbcontext = new ApplicationDBContext(options);
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 1,PurchaseId = 1,Cost = 10 });
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 1, PurchaseId = 2, Cost = 10 });
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 1, PurchaseId = 3, Cost = 10 });
await dbcontext.CustomerPurchases.AddAsync(new CustomerPurchase { CustomerId = 2, PurchaseId = 2, Cost = 10 });
await dbcontext.SaveChangesAsync();
var filters = new List<Filter>();
filters.Add(new Filter { CustomerId = 1, PurchaseId = 2 });
filters.Add(new Filter { CustomerId = 2, PurchaseId = 2 });
var resq = await dbcontext.CustomerPurchases
.Where(p=> filters.Contains(new Filter { CustomerId = p.CustomerId,PurchaseId = p.PurchaseId }))
.ToListAsync();
foreach (var item in resq)
{
Console.WriteLine($" CustomerId : {item.CustomerId} , PurchaseId : {item.PurchaseId} Cost : {item.Cost}");
}
var res1 = dbcontext.CustomerPurchases
.Where(p => filters.Select(c=>c.PurchaseId).Contains(p.PurchaseId))
.Where(p => filters.Select(c => c.CustomerId).Contains(p.CustomerId));
var res = await res1.ToListAsync();
Console.WriteLine("===========================================================");
foreach (var item in res)
{
Console.WriteLine($" CustomerId : {item.CustomerId} , PurchaseId : {item.PurchaseId} Cost : {item.Cost}");
}
和运行ning代码
更新 更改为 SQL 服务器后,我仍然遇到错误,因此当 运行 在 SQL 服务器
上运行时,选项 2 不是一个选项但我找到了另一个解决方案,我可以根据我拥有的过滤器列表构建 where 子句 我找到这个 PredicateBuilder 这是使用 predicate builder
的代码var whereclause = PredicateBuilder.False<CustomerPurchase>();
foreach (var filterrow in filters)
{
whereclause = whereclause.Or(c => c.CustomerId == filterrow.CustomerId && c.PurchaseId == filterrow.PurchaseId);
}
var resqq = dbcontext.CustomerPurchases.Where(whereclause);
var resq = await resqq.ToListAsync();
foreach (var item in resq)
{
Console.WriteLine($" CustomerId : {item.CustomerId} , PurchaseId : {item.PurchaseId} Cost : {item.Cost}");
}
这将构建查询,该查询将由 sql 转换为以下语句
DECLARE @__filterrow_CustomerId_0 int = 1;
DECLARE @__filterrow_PurchaseId_1 int = 2;
DECLARE @__filterrow_CustomerId_2 int = 2;
DECLARE @__filterrow_PurchaseId_3 int = 2;
SELECT [c].[PurchaseId], [c].[CustomerId]
FROM [dbo].[CustomerPurchase] AS [c]
WHERE (([c].[CustomerId] = @__filterrow_CustomerId_0) AND ([c].[PurchaseId] = @__filterrow_PurchaseId_1))
OR
(([c].[CustomerId] = @__filterrow_CustomerId_2) AND ([c].[PurchaseId] = @__filterrow_PurchaseId_3))
这是 PredicateBuyilder
的完整 classpublic static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
希望能回答你的问题!!