我使用的是带有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);
}