代码之家  ›  专栏  ›  技术社区  ›  Flo

.net核心-将func列表与单个func合并或合并到单个func

  •  1
  • Flo  · 技术社区  · 7 年前

    var funcs = new List<Func<User, bool>>()
    {
        (u) => u.Id.Equals(entityToFind.Id),
        (u) => u.UserName == entityToFind.UserName,
        (u) => u.Email == entityToFind.Email
    };
    
    //TODO: Some magic that funs is euqaly to that:
    
    Func<User, bool> func =  (u) => u.Id.Equals(entityToFind.Id) || u.UserName == entityToFind.UserName || u.Email == entityToFind.Email;
    

    我也试过表达,比如:

    private Dictionary<string, Expression<Func<User, bool>>>     private Dictionary<string, Expression<Func<User, bool>>> test(User entityToFind)
    {
        return new Dictionary<string, Expression<Func<User, bool>>>() {
            {"Id", (u) => u.Id.Equals(entityToFind.Id) },
            {"Name", (u) => u.UserName == entityToFind.UserName },
            {"Email", (u) => u.Email == entityToFind.Email }
        };
    }
    
    
    public static Expression<Func<T, bool>> ToOrExpression<T>(this Dictionary<string, Expression<Func<T, bool>>> dict)
    {
        var expressions = dict.Values.ToList();
        if (!expressions.Any())
        {
            return t => true;
        }
    
        var delegateType = typeof(Func<T, bool>)
            .GetGenericTypeDefinition()
            .MakeGenericType(new[]
                {
                    typeof(T),
                    typeof(bool)
                }
            );
    
        var tfd = Expression.OrElse(expressions[0], expressions[1]);
    
        var combined = expressions
            .Cast<Expression>()
            .Aggregate( (e1, e2) => Expression.OrElse(e1, e2) );
    
        return (Expression<Func<T, bool>>)Expression.Lambda(delegateType, combined);
    }
    
    
    test(entityToFind).ToOrExpression();
    

    但我会得到以下错误:

    未为类型“”定义二进制运算符OrElse系统功能 2[Models.User,System.Boolean]' and 'System.Func 2[模型.用户,系统.布尔]'

    1 回复  |  直到 7 年前
        1
  •  7
  •   DavidG    7 年前

    Func s、 因为您使用的是实体框架,这将导致整个数据集被下载到内存中,搜索在本地完成。你应该用的是 Expression<Func<T, bool>> 相反。

    幸运的是 Marc Gravell already written a handy bit of code to combine expressions . 你的问题严格来说是双重的,因为你想把两个以上的问题组合在一起,但这很容易用一点Linq。所以,让我们先从表达式开始,代码几乎没有变化:

    var expressions = new List<Expression<Func<User, bool>>>()
    {
        (u) => u.Id.Equals(entityToFind.Id),
        (u) => u.UserName == entityToFind.UserName,
        (u) => u.Email == entityToFind.Email
    };
    

    现在使用Marc的代码 or and :

    public static class ExpressionExtensions
    {
        public static Expression<Func<T, bool>> OrElse<T>(
            this Expression<Func<T, bool>> expr1,
            Expression<Func<T, bool>> expr2)
        {
            var parameter = Expression.Parameter(typeof(T));
    
            var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
            var left = leftVisitor.Visit(expr1.Body);
    
            var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
            var right = rightVisitor.Visit(expr2.Body);
    
            return Expression.Lambda<Func<T, bool>>(
                Expression.OrElse(left, right), parameter);
        }
    
        private class ReplaceExpressionVisitor
        : ExpressionVisitor
        {
            private readonly Expression _oldValue;
            private readonly Expression _newValue;
    
            public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
            {
                _oldValue = oldValue;
                _newValue = newValue;
            }
    
            public override Expression Visit(Expression node)
            {
                if (node == _oldValue)
                    return _newValue;
                return base.Visit(node);
            }
        }
    }
    

    不,你把你的表情和Linq结合起来 Aggregate 方法:

    var combinedExpression = expressions.Aggregate((x, y) => x.OrElse(y));
    

    然后像这样使用它:

    var result = db.Things.Where(combinedExpression);