如何将变量传递给谓词表达式?

How to pass a variable to predicate expression?

我正在动态创建 Expression<Func<Entity, bool>> 表达式树:

int id = ...;
Expression<Func<Entity, int>> keySelector = e => e.Id;
BinaryExpression equals = Expression.Equal(
   keySelector.Body, 
   Expression.Constant(id, keySelector.Body.Type)); //replace this with a variable

var predicate = Expression.Lambda<Func<Entity, bool>>(equals, keySelector.Parameters[0]);

相当于 e => e.Id == 1.

如何传递变量而不是常量:e => e.Id == id

您需要将常量更改为可以更改 属性 的内容:

class IDHolder
{
    public int Id { get; set; }
}

现在您将表达式绑定到 IDHolder 的实例,如下所示:

var idHolder = new IDHolder { Id = 0 };
Expression<Func<Entity, int>> keySelector = e => e.Id;

BinaryExpression equals = Expression.Equal(
    keySelector.Body,
           
    Expression.Property(
        Expression.Constant(idHolder, typeof(IDHolder)),
    nameof(IDHolder.Id))
           
    );

var predicate = Expression.Lambda<Func<Entity, bool>>(equals, keySelector.Parameters[0]);

谓词变得稍微复杂一些,但不会使 EF 中断:

x => x.Id == idHandler.Id

您可以通过编译谓词和 运行 更改 Id 和实体的简单测试来测试它:

var func = predicate.Compile();

idHolder.Id = 5;

Console.WriteLine(func(new Entity { Id = 0 }).ToString());  // false
Console.WriteLine(func(new Entity { Id = 5 }).ToString());  // true

idHolder.Id = 6;

Console.WriteLine(func(new Entity { Id = 6 }).ToString());   // true

请注意,此方法不是线程安全的,如果您缓存谓词和 idHolder 并将其用于 Web api 项目或任何可能同时使用此谓词的项目,将会导致奇怪的行为。要自动翻译请求中的某些条件,最好在每次需要在 EF 中使用它时创建表达式。创建表达式时不会对性能造成重大影响;昂贵的操作是编译,而 EF 永远不会这样做。


或者我误解了您的问题,您只需要一种方法来更改 id 的值,正如@Jochem Van Hespen 所建议的,您需要一个函数:

public Expression<Func<Entity,bool>> GetPredicate(int id){

    Expression<Func<Entity, int>> keySelector = e => e.Id;
    BinaryExpression equals = Expression.Equal(
       keySelector.Body, 
       Expression.Constant(id, keySelector.Body.Type)); 

    var predicate = Expression.Lambda<Func<Entity, bool>>(equals, keySelector.Parameters[0]);
    return predicate;
    }

你可以这样使用它:

 _dbContext.Set<Entity>().Where(GetPredicate(4))