EF Core中如何用List<BucketList>多条件匹配查询SQL表数据
问题描述
需要使用Entity Framework Core通过LINQ从对应实体类Table1EntityClass的SQL表中,查询与List<BucketList>对象组合条件匹配的数据。
实体类定义
public class Table1EntityClass { public int id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { get; set; } public int Quantity { get; set; } } public class BucketList { public string Name { get; set; } public string Price { get; set; } public string Category { get; set; } }
需求说明
查询table1中Name、Price、Category与List
示例数据:
表中数据:
- Name: Apple, Description: A fruit, Price: 30, Category: fruits, Quantity:10
- Name: Apple, Description: A fruit, Price:20, Category: fruits, Quantity:10
- Name: Orange, Description: A fruit, Price:50, Category: fruits, Quantity:10
- Name: banana, Description: A fruit, Price:30, Category: fruits, Quantity:10
- Name: Onion, Description: A vegetable, Price:30, Category: vegetable, Quantity:5
List
包含对象:
- Apple, 30, fruits
- Onion, 30, vegetable
期望结果:第1行和第5行数据。
尝试的LINQ查询及错误
尝试了以下代码,但EF Core无法翻译该查询:
var result = dbContext.table1 .Where(item => bucketLists.Any(bucketvalue => item.Name == bucketvalue.Name && item.Price == bucketvalue.Price && item.Category == bucketvalue.Category)) .ToList();
错误提示:LINQ查询无法被翻译,需改写为可翻译形式,或显式调用AsEnumerable等启用客户端评估。
解决方案
前置注意事项
首先要处理类型不匹配问题:BucketList的Price是string类型,而Table1EntityClass的Price是decimal,需要先把BucketList中的Price转换为decimal(可根据实际场景添加转换失败的异常处理),避免后续查询出错。
方法1:构建匿名类型条件集合,使用Contains匹配组合条件
先将bucketLists转换为包含Name、Price(已转decimal)、Category的匿名类型集合,再用Contains匹配组合条件。EF Core能将这种写法翻译为SQL中的组合IN子句,完全在数据库端执行:
// 转换BucketList并处理Price类型,假设Price均可正常转换,可按需添加异常捕获 var filterItems = bucketLists.Select(b => new { b.Name, Price = decimal.Parse(b.Price), b.Category }).ToList(); var result = dbContext.table1 .Where(item => filterItems.Contains(new { item.Name, item.Price, item.Category })) .ToList();
方法2:动态构建OR条件组合
如果匿名类型的方式不适用,可手动构建多个OR条件,每个条件对应BucketList中的一个对象:
var predicate = PredicateBuilder.False<Table1EntityClass>(); foreach (var bucket in bucketLists) { var name = bucket.Name; var price = decimal.Parse(bucket.Price); var category = bucket.Category; predicate = predicate.Or(item => item.Name == name && item.Price == price && item.Category == category); } var result = dbContext.table1.Where(predicate).ToList();
这里需要用到PredicateBuilder,可以自己实现一个简易版本:
public static class PredicateBuilder { public static Expression<Func<T, bool>> False<T>() => 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); } }
方法3:客户端过滤(仅小数据量场景使用)
如果表数据量极小,也可以先将全表数据加载到内存,再在客户端过滤,但这种方式会占用较多内存,大数据场景下性能极差,谨慎使用:
var result = dbContext.table1.AsEnumerable() .Where(item => bucketLists.Any(bucketvalue => item.Name == bucketvalue.Name && item.Price == decimal.Parse(bucketvalue.Price) && item.Category == bucketvalue.Category)) .ToList();
内容的提问来源于stack exchange,提问作者Pratyaksh
相关产品推荐
相关产品推荐

