没有PredicateBuilder的LINQ中的动态OR [英] Dynamic OR in LINQ without the PredicateBuilder

查看:70
本文介绍了没有PredicateBuilder的LINQ中的动态OR的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一种方法,该方法采用一个或多个条件来使用LINQ查询数据库.我做到了:

I am building a method that takes one or more criteria for querying a database with LINQ. I made this:

public ICollection<MyClass> FindAllBy(params Expression<Func<MyClass, bool>>[] criteria)
    {
        using (var ctx = new MyContext())
        {
            IQueryable<MyClass> result = ctx.MyClasses.AsNoTracking();

            if (criteria != null && criteria.Length > 0)
            {
                foreach (var item in criteria)
                {
                    result = result.Where(item);
                }
            }

            return result.ToList();
        }
    }

这样的结果是,如果我寻找一个ID为1的对象而一个ID为2的对象,我什么也得不到,因为没有行的ID分别为1和2.所以我需要一个OR子句.我找到此页面:

This has the effect that if I look for a object with Id 1 and one with Id 2 I get nothing, as no row has both an Id of 1 and 2. So I need an OR clause. I found this page:

http://www.albahari.com/nutshell/predicatebuilder.aspx

其中有一个PredicateBuilder类,我曾经用它来制作:

Which has a PredicateBuilder class, which I used to make this:

    public ICollection<PhotoFile> FindAllBy(params Expression<Func<PhotoFile, bool>>[] criteria)
    {
        using (var ctx = new CotanContext())
        {
            var predicate = PredicateBuilder.False<PhotoFile>();

            if (criteria != null && criteria.Length > 0)
            {
                foreach (var item in criteria)
                {
                    predicate = predicate.Or(item);
                }
            }

            return ctx.PhotoFiles.Where(predicate).ToList();
        }
    }

我的代码与页面略有不同,因为我将表达式传递给方法,然后将其传递给Predicate.Or方法.

My code differs slightly from the page in that I pass in an expression into the method, which I then pass into the Predicate.Or method.

上述方法给出The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.错误.这很有意义,因为Entity Framework不知道如何将此代码转换为有效查询.

The above method gives a The LINQ expression node type 'Invoke' is not supported in LINQ to Entities. error. Which makes sense, as Entity Framework does not know how to translate this code into a valid query.

链接站点上的解决方案是下载其Nuget程序包或源代码,从而使此代码有效.但是,我对于为单个函数输入几百行未知且似乎未经测试的代码并不感到很自在,我认为Microsoft应该早在LINQ时代就已经内置了这些代码.而且,我项目的首席开发人员过去也强烈建议不要使用不是直接来自Microsoft的未知软件包.我正在处理敏感信息,所以我宁愿安全也不愿后悔.

The solution on the linked site is to download their Nuget package or source code, which makes this code work. However, I don't really feel comfortable putting in several hundreds of lines of unknown and seemingly untested code for a single function, that in my opinion should have been built into LINQ ages ago by Microsoft. And the lead developer on my project has also in the past strongly advised against using unknown packages that aren't directly from Microsoft. I am working with sensitive information, so I would rather be safe than sorry.

所以,我的问题是:有没有办法在LINQ中获得OR功能而不必使用外部Nuget包?

So, my question: is there any way to get an OR function in LINQ without having to use an external Nuget package?

推荐答案

正如我在评论中提到的,您可以使用在linq中的两个列表之间建立指向where子句的实体的链接.

As I mentioned in the comments, you can use the Universal PredicateBulder or the class from my answer to Establish a link between two lists in linq to entities where clause.

但是,通过使用这种简单的扩展方法,您可以大大简化示例中的方法:

However you can greatly simplify the methods like the one from your example by using this simple extension method:

public static class QueryableExtensions
{
    public static IQueryable<T> WhereAny<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, bool>>> predicates)
    {
        if (predicates == null || !predicates.Any()) return source;
        var predicate = predicates.Aggregate((a, b) => Expression.Lambda<Func<T, bool>>(
            Expression.OrElse(a.Body, b.Body.ReplaceParameter(b.Parameters[0], a.Parameters[0])),
            a.Parameters[0]));
        return source.Where(predicate);
    }
}

依次使用此帮助程序:

public static class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == Source ? Target : base.VisitParameter(node);
        }
    }
}

现在示例方法可能很简单:

Now the sample method could be simple as that:

public ICollection<PhotoFile> FindAllBy(params Expression<Func<PhotoFile, bool>>[] criteria)
{
    using (var ctx = new CotanContext())
        return ctx.PhotoFiles.WhereAny(criteria).ToList();
}

这篇关于没有PredicateBuilder的LINQ中的动态OR的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆