目录
咨询区
Eduard:
我想知道如何在 EntityFramework 或者 Linq 上构建动态查询。
我现在要做这么一个功能,UI上大概有 50+
的查询条件,用户可以根据自己的需求勾选所需的条件传到我的后端服务方法中。
我知道可以通过 ExpresstionTree
去动态拼接这样的条件,但我不喜欢这种方法,我想知道有没有更简单的方式,并且类型安全。
回答区
Slauma:
你可以一步一步的组合 IQueryable<T>
,起初你可以有一个 FilterDefinition
类表示可构建的 Query 实体,类定义如下:
public class FilterDefinition
{
public bool FilterByName { get; set; }
public string NameFrom { get; set; }
public string NameTo { get; set; }
public bool FilterByQuantity { get; set; }
public double QuantityFrom { get; set; }
public double QuantityTo { get; set; }
}
然后你就可以像下面这样一步步构建 Query 语句。
public IQueryable<SomeEntity> GetQuery(FilterDefinition filter)
{
IQueryable<SomeEntity> query = context.Set<SomeEntity>();
// assuming that you return all records when nothing is specified in the filter
if (filter.FilterByName)
query = query.Where(t =>
t.Name >= filter.NameFrom && t.Name <= filter.NameTo);
if (filter.FilterByQuantity)
query = query.Where(t =>
t.Quantity >= filter.QuantityFrom && t.Quantity <= filter.QuantityTo);
return query;
}
Gurmit Teotia:
我自己封装了一个 泛型仓储
[https://github.com/gurmitteotia/EFDataAccess] , 我觉得对你应该有用,它提供了统一化的API接口,可参考下面的例子。
//Filter on known fields
var keyboard = Query<Product>.Create(p=>p.Category=="Keyboard");
var keyboards = repository.Get(keyboard);
//Or filter on dynamic fields
var filter = Query<Product>.Create("Rating", OperationType.GreaterThan, 4)
var filteredKeyboards = repository.Get(filter);
//You can also combine two queries togather
var filterdKeyboards2 = repository.Get(keyboard.And(filter))
//Order it on known fields
var orderedKeyboard = keyboard.OrderBy(o=>o.Asc(p=>p.Name));
var orderedKeyboards = repository.Get(orderedKeyboard);
//Or order by on dynamic fields
var userOrdering = keyboard.OrderBy(o=>o.Asc("Name"));
var orderedKeyboards2 = repository.Get(userOrdering);
虽然我不知道你的查询 DTO,但你可以很容易的构建泛型 Query 并塞入你的 DTO 实体,我已经用它好多年了,绝对好用。
点评区
在纯sql时代,这个需求很简单,不断的 +=
拼接即可,反而在 强类型
下却不是那么好做了,不过我觉得 Gurmit Teotia
大佬封装的 Query 框架还挺好用的,支持一下。