我将在前面加上我正在积极寻找解决这个问题的方法,但我想如果有人在堆栈上找到这个问题,我可能会缩短一些研究和开发时间。 (我在网上找不到任何东西,所以这里有)
我们在一个应用程序框架中有一个案例,我们需要能够接受一组Predicates(List<Expression<Func<T,bool>>>
)并在搜索框架中解析它。
现在我们有能力以这种方式过滤:
//Assume predicates is passed as a method argument.
// of List<Expression<Func<T,bool>>>
//Assume user is passed in as a method argument.
//Assume FilterToUserAccess is a custom extension method that restricts the dataset
// to access restrictions.
var query = _dbContext.Set<EntityType>()
.FilterToUserAccess(user);
foreach(var p in predicates){
query = query.Where(p);
}
return p.ToList();
我们需要这样做的原因是可过滤对象的可扩展性。但是,对于快速搜索,鉴于EF的内置功能,这是不可能的。我需要做的是:
对象A(让我们假装它是一辆赛车)我们希望在快速搜索框中搜索品牌,型号,团队和驱动程序。因此,如果我输入“Earnhardt”,它将搜索所有赛车实体属性,包括品牌,型号,团队和驱动程序。我最终会得到所有的DEI汽车以及Dale Jr.我想使用相同的方法,因此我们可以配置一个可搜索的实体并在应用程序启动时反映搜索配置。理想情况下,我想让查询看起来像这样:
//Assume predicates is passed as a method argument.
// of List<Expression<Func<T,bool>>>
//Assume user is passed in as a method argument.
//Assume FilterToUserAccess is a custom extension method that restricts the dataset
// to access restrictions.
var query = _dbContext.Set<EntityType>()
.FilterToUserAccess(user);
foreach(var p in predicates){
query = query.Or(p);
}
return p.ToList();
我意识到我能做到:
_dbContext.Set<EntityType>().Where(predicate1 || predicate2 || predicate3)
但是,这对我们想要解决此问题的方法不起作用。理想情况下,我们的一个客户端站点的管理员可以进入并配置一个额外的搜索词,只需单击即可包含在该实体类型的任何和所有快速搜索中,就像我们目前可以使用标准的过滤器一样.Where(...)
“和”链接逻辑。
答案 0 :(得分:6)
第一个解决方案是破产,但是通过更多挖掘,有一个非常简单的解决方案,经过验证并且有效。
第1步:为LinqKit安装NuGet包。
第2步:享受以下代码
using (ISampleRepository repo = new SampleRepository())
{
var predicates = new List<Expression<Func<Customer,bool>>>(){
(x => x.FirstName.Contains(searchValue)),
(x => x.LastName.Contains(searchValue))
};
var lambda = PredicateBuilder.False<Customer>();
lambda = predicates.Aggregate(lambda, (current, p) => current.Or(p).Expand());
var query = repo.QueryCustomers().AsExpandable().Include(x => x.Phones).Where(lambda);
return query.Take(500)
.ToList()
.Select(x => x.ToDTO())
.ToList();
}
这只是尖峰样本,但采用的方法做了同样的事情 - &gt;
List<T> QuickSearch<T>(string input) ...
将能够使用相同的方法。您仍然以传递的Expression形式存在一组谓词,然后使用谓词构建器技巧来关闭查询。然后使用AsExpandable()允许您执行使用谓词构建器创建的组合谓词。
希望这不仅仅对我有帮助,但这是我所采用的解决方案,因为它的代码要少得多。允许您在其他地方构建谓词...并仍然将它们组合在一个&#34; OR&#34;事后陈述。
答案 1 :(得分:2)
正如Ladislav所说,您需要动态生成LINQ表达式。下面是一个动态构建整数集合谓词的程序的简单示例:
class Program {
static void Main(string[] args) {
// Retreive your data source
List<int> numbers = new List<int>() { 0, 10, 20, 30, 40, 50, 60 };
// Create a collection of predicates that you would like to chain together.
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x");
List<Expression> predicates = new List<Expression>();
// x >= 50
predicates.Add(Expression.GreaterThanOrEqual(parameterExpression, Expression.Constant(50)));
// x <= 20
predicates.Add(Expression.LessThanOrEqual(parameterExpression, Expression.Constant(20)));
// Build a single predicate by chaining individual predicates together in an OR fashion
Expression whereFilter = Expression.Constant(false); // Use false a base expression in OR statements
foreach (var predicate in predicates) {
whereFilter = Expression.OrElse(whereFilter, predicate);
}
// Once the expressions have been chained, create a lambda to represent the whole predicate
// x => (x >= 50) || (x <= 20)
Expression<Func<int, bool>> whereLambda =
(Expression<Func<int, bool>>)Expression.Lambda(whereFilter,
new List<ParameterExpression>() { parameterExpression });
// To use an expression directly, the datasource must be an IQueryable
// Since I am using List<T> I must call AsQueryable. This is not necessary
// if your collection is already IQueryable, like in Entity Framework.
var results = numbers.AsQueryable().Where(whereLambda);
}
}
基本上我在这里做的就是创建几个布尔值(x&gt; = 50)和(x <= 20)并将它们放在一个集合中。然后通过循环遍历该集合,我将每个语句和OR一起转到最后一个语句。结果是一系列布尔语句都通过OR链接在一起。然后我将该语句包装在Lambda表达式中,以便IQueryable.Where
可以使用它并将其传递给我的可查询集合。结果是我的全套过滤的整数集。
LINQ表达式至少可以说令人困惑,但它们非常强大且值得知道。如果我能做些什么来帮助我更好地理解这个例子,请告诉我。