组合多个表达式树

本文关键字:表达式 组合 | 更新日期: 2023-09-27 18:22:44

我得到以下错误

参数"p"未绑定在指定的LINQ to Entities中查询表达式。

我理解这个问题(ParameterExpression的同一实例应该与树中的所有表达式一起使用),并尝试使用我在网上找到的解决方案,但没有成功。

这是我的方法

private void SeedEntity<TEntity>(DatabaseContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) where TEntity : class
{
    Expression<Func<TEntity, bool>> allExpresions = null;
    var parameters = identifierExpressions.SelectMany(x => x.Parameters).GroupBy(x => x.Name).Select(p => p.First()).ToList();
    foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions)
    {
        Func<TEntity, object> vv = identifierExpression.Compile();
        object constant = vv(entity);
        ConstantExpression constExp = Expression.Constant(constant, typeof(object));
        BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp);
        Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, parameters);
        if (allExpresions == null)
        {
            allExpresions = equalExpression2;
        }
        else
        {
            BinaryExpression bin = Expression.And(allExpresions.Body, equalExpression2.Body);
            allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, parameters);
        }
    }
    TEntity existingEntity = null;
    if (allExpresions != null)
    {
        existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions);
    }
    if (existingEntity == null)
    {
        context.Set<TEntity>().Add(entity);
    }
    else
    {
        entity = existingEntity;
    }
}

它生成一个表达式,用于基于多个特性查找实体。

它适用于单个表达式,只有在传入多个表达式时才会出现错误。

这样调用:

SeedEntity(context, ref e, p=> p.Name);//Works
SeedEntity(context, ref e, p=> p.Name, p=> p.Age);//Fails

它产生了类似于我执行以下操作的东西:

context.Set<TEntity>().FirstOrDefault(p=>p.Name == e.Name && p.Age == e.Age);

替换e.名称&amp;e.ConstantExpression 年龄

您可以在上面的方法中看到,我获取所有唯一的param,并将它们存储在顶部的parameters中,然后在整个过程中使用相同的变量。这是开始,但我需要替换作为params数组传入的每个Expression<Func<TEntity, bool>>中的参数实例,这就是我失败的地方。

我已经尝试枚举表达式,并使用传入params 的.Update()方法

我还尝试了使用ExpressionVisitor 的解决方案

public class ExpressionSubstitute : ExpressionVisitor
{
    public readonly Expression from, to;
    public ExpressionSubstitute(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        if (node == from) return to;
        return base.Visit(node);
    }
}
public static class ExpressionSubstituteExtentions
{
    public static Expression<Func<TEntity, TReturnType>> RewireLambdaExpression<TEntity, TReturnType>(Expression<Func<TEntity, TReturnType>> expression, ParameterExpression newLambdaParameter)
    {
        var newExp = new ExpressionSubstitute(expression.Parameters.Single(), newLambdaParameter).Visit(expression);
        return (Expression<Func<TEntity, TReturnType>>)newExp;
    }
}

组合多个表达式树

你真的很接近。我看不出你的parameters变量有什么意义。按名称分组是错误的。为什么不直接传递表达式中的参数呢?如有必要,请访问。您的访问者代码很好。

    private static void SeedEntity<TEntity>(DbContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) 
        where TEntity : class
    {
        Expression<Func<TEntity, bool>> allExpresions = null;
        foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions)
        {
            Func<TEntity, object> vv = identifierExpression.Compile();
            object constant = vv(entity);
            ConstantExpression constExp = Expression.Constant(constant, typeof(object));
            BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp);
            Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, identifierExpression.Parameters);
            if (allExpresions == null)
            {
                allExpresions = equalExpression2;
            }
            else
            {
                var visitor = new ExpressionSubstitute(allExpresions.Parameters[0], identifierExpression.Parameters[0]);
                var modifiedAll = (Expression<Func<TEntity,bool>>)visitor.Visit(allExpresions);
                BinaryExpression bin = Expression.And(modifiedAll.Body, equalExpression2.Body);
                allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, identifierExpression.Parameters);
            }
        }
        TEntity existingEntity = null;
        if (allExpresions != null)
        {
            existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions);
        }
        if (existingEntity == null)
        {
            context.Set<TEntity>().Add(entity);
        }
        else
        {
            entity = existingEntity;
        }
    }