7

我会以我正在积极寻找这个问题的解决方案作为序言,但我认为如果堆栈中的某个人已经解决了这个问题,我可能会缩短一些研发时间。(我在网上没有找到任何东西,所以这里是)

在我们正在构建的应用程序框架中有一个案例,我们需要能够接收一组谓词 ( 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(...)“和”链接逻辑。

4

2 回答 2

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();
}

这只是尖峰样本,但用一个方法做同样的事情 ->

List<T> QuickSearch<T>(string input) ...

将能够使用相同的方法。您有一组仍以表达式形式传入的谓词,然后您使用谓词构建器技巧来拉出查询。然后使用 AsExpandable() 允许您执行使用谓词构建器创建的组合谓词。

希望这不仅对我有帮助,但这是我要使用的解决方案,因为它的代码要少得多。允许您在其他地方构建您的谓词......并且在事后仍然将它们组合在“OR”语句中。

于 2012-08-27T19:11:32.223 回答
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 >= 50) 和 (x <= 20) 并将它们放在一个集合中。然后通过循环遍历该集合,我将每个语句和 OR 到最后一个语句。结果是一系列通过 OR 链接在一起的布尔语句。然后我将该语句包装在一个 Lambda 表达式中,以便它可以被使用IQueryable.Where并将其传递给我的可查询集合。结果是从我的完整集合中过滤的一组整数。

LINQ 表达式至少可以说令人困惑,但它们非常强大且值得了解。请让我知道是否有任何我可以做的事情来帮助理解这个例子。

于 2012-08-27T19:12:03.997 回答