实体框架核心Lambda函数未提前终止



我使用的是带有entityframework的asp.net核心。

当进行API调用以获取实体列表时,我在json中传递了一个可选的过滤器,看起来如下:

{
"name": "john",
"customer.id": 1
}

在ControllerAction中,我首先对JSON进行反序列化,然后对DB进行查询。

var dynamicfilter = filter != null ? JsonConvert.DeserializeObject<Dictionary<string, string>>(filter) : new Dictionary<string, string>();
List<User> Users = _context.Users.Include(u => u.Customer).Where(u =>
dynamicfilter.Keys.Count > 0 ? (
(dynamicfilter.ContainsKey("name") ? u.Name.ToLower().Contains(dynamicfilter["name"].ToLower()) : true) &&
(dynamicfilter.ContainsKey("customer.id") ? u.Customer.ID == Convert.ToInt32(dynamicfilter["customer.id"]) : true)
) : true).ToList();

只要设置了所有的过滤器参数,这就可以工作。如果至少有一个筛选器参数未设置,则会抛出null引用,因为即使ContainsKey()false,代码之间的关系也是?和:执行。

但是,如果我首先加载整个表并在本地应用过滤器,就像一样

List<User> Users = _context.Users.Include(u => u.Customer).ToList().Where(...

它就像一个符咒。有没有办法让它也在数据库端工作?

只需将逻辑移出查询。类似:

IQueryable<User> query = _context.Users.Include(u => u.Customer);
query = dynamicfilter.ContainsKey("name") 
? query.Where(u=>u.Name.ToLower().Contains(dynamicfilter["name"].ToLower()) 
: query;
query = dynamicfilter.ContainsKey("customer.id") 
? query.Where(u=>u.Customer.ID == Convert.ToInt32(dynamicfilter["customer.id"])) 
: query;
List<User> Users = query.ToList(); // DB query executed here

这样,表达式树将在query对象中构建,并且仅在枚举尝试时执行。检查MS文档

此接口继承IEnumerable接口,因此如果它表示查询,则可以枚举该查询的结果。枚举强制执行与IQueryable对象关联的表达式树。调用Execute(Expression(方法时,将执行不返回可枚举结果的查询。

最重要的是,我建议在将其应用于查询之前检查所有传入的参数

if(dynamicfilter.ContainsKey("name") 
&& !string.IsNullOrWhitespace(dynamicfilter["name"]))
{
var nameFilter = dynamicfilter["name"].ToLower();
query = query.Where(u=>u.Name.ToLower().Contains(nameFilter);
}

最新更新