【问题标题】:.net core - combine a list of func with or to a single func.net 核心 - 将 func 列表与单个 func 组合或组合
【发布时间】:2019-02-10 21:18:30
【问题描述】:

您好,我尝试从由 or 组合的列表中生成单个 Func。

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 没有为类型 'System.Func2[Models.User,System.Boolean]' and 'System.Func2[Models.User,System.Boolean]' 定义

【问题讨论】:

  • 这是否适用于实体框架?
  • @DavidG 是的,对于 MongoClient
  • 所以你已经使用Func而不是Expression处于一个糟糕的前提
  • 我将使用:return u => generatedFunc(entityToFind);最后;)
  • @DavidG 但如果您知道使用表达式的解决方案,我也可以这样做

标签: linq generics asp.net-core .net-core func


【解决方案1】:

虽然您可以创建一个包装方法来组合一堆Funcs,但因为您使用的是实体框架,这将导致整个数据集被下载到内存中并在本地完成搜索。你应该使用的是Expression&lt;Func&lt;T, bool&gt;&gt;

幸运的是Marc Gravellalready written a handy bit of code to combine expressions。您的问题严格来说是重复的,因为您想将超过 2 个组合在一起,但是使用一点 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);

【讨论】:

  • 哇!非常感谢!这是有效的,现在我只尝试了解它是如何工作的......;)
猜你喜欢
  • 2019-11-26
  • 1970-01-01
  • 2010-10-02
  • 2012-11-11
  • 1970-01-01
相关资源
最近更新 更多