Entity framework core 使用现有表达式/func生成另一个表达式/func

Entity framework core 使用现有表达式/func生成另一个表达式/func,entity-framework-core,c#,generics,lambda,expression,Entity Framework Core,C#,Generics,Lambda,Expression,我希望能够使用泛型方法来选择属性并将其传递到方法中 运算符“==”不能应用于“TProperty”和“TProperty”类型的操作数 我错过了什么 这个问题更多的是一个人为的例子,因为表达式最终将被用来构建查询。如果问题是如何从表达式构建表达式,以及表示相等谓词的TModel,可以这样做: // Expression<Func<TModel, TProperty>> propertySelector // TModel model var parameter = pro

我希望能够使用泛型方法来选择属性并将其传递到方法中

运算符“==”不能应用于“TProperty”和“TProperty”类型的操作数

我错过了什么


这个问题更多的是一个人为的例子,因为表达式最终将被用来构建查询。

如果问题是如何从
表达式
构建
表达式
,以及表示相等谓词的
TModel
,可以这样做:

// Expression<Func<TModel, TProperty>> propertySelector
// TModel model
var parameter = propertySelector.Parameters[0];
var left = propertySelector.Body;
var right = Expression.Invoke(propertySelector, Expression.Constant(model));
var body = Expression.Equal(left, right);
var predicate = Expression.Lambda<Func<TModel, bool>>(body, parameter);
其中,
ReplaceParameter
是基于
ExpressionVisitor
的常用帮助程序,用于将
ParameterExpression
替换为另一个任意表达式(非常类似于
string.Replace
,但使用表达式):


问题中的
Any
方法看起来像是源序列上的扩展方法,但缺少目标对象的参数

从概念上讲,这就是我认为您希望做的:

public static bool Any(此IEnumerable源代码、Func选择器、tenty other)
{
if(源为空)
抛出新ArgumentNullException(nameof(source));
如果(选择器为空)
抛出新ArgumentNullException(nameof(selector));
如果(其他==null)
抛出新的ArgumentNullException(nameof(other));
TProperty otherProperty=选择器(其他);
返回source.Any(item=>EqualityComparer.Default.Equals(选择器(item),otherProperty));
}
现在,这将对内存中的对象起作用,但是由于您有标记,我们需要一些处理表达式的东西

要做到这一点,我们必须首先对属性求值,以获得比较每个实体所选属性的值。在内存中,我们只需调用
选择器(其他)
,就完成了。因为我们现在必须处理一个表达式,所以我们需要首先编译这个表达式。这相当简单

下一步是构建表示
选择器(项)==otherValue
的表达式。幸运的是,我们不需要替换参数,因为我们不想将两个单独的lambda折叠到同一个表达式中。我们可以简单地重用选择器的第一个参数。然后我们调用
Expression.Invoke
,使用选择器及其参数,这些参数将被转换为SQL中的列引用

最后,我们构建lambda,我们可以将其传递给内置的
Any
方法

public static bool Any(此IQueryable源代码、表达式selector表达式、其他)
{
if(源为空)
抛出新ArgumentNullException(nameof(source));
如果(selectorExpression为空)
抛出新ArgumentNullException(nameof(selectorExpression));
如果(其他==null)
抛出新的ArgumentNullException(nameof(other));
ParameterExpression itemParameter=selectorExpression.Parameters[0];
ConstantExpression otherValue=Expression.Constant(selectorExpression.Compile()(其他),typeof(TProperty));
BinaryExpression equalExpression=Expression.Equal(Expression.Invoke(selectorExpression,itemParameter),otherValue);
表达式谓词=Expression.Lambda(equalExpression,itemParameter);
返回source.Any(谓词);
}

在我自己的测试中,使用SQL Profiler进行确认,我能够在SQL查询中获得要计算的条件。更改属性选择表达式后,查询也相应更改。

我猜
属性选择器(模型)
在迭代
\u models
期间不会更改。你能做些什么,把
m=>propertySelector(m)
作为一个表达式,把
propertySelector(model)
的结果作为一个属性吗?我正在考虑将前一个表达式与后一个表达式组合为
equalpression
中的
ConstantExpression
,并从中创建一个委托。您在
属性选择器(模型)
上的回答是正确的。我不太清楚你的第二句话到底是什么意思,也不清楚为什么
t属性的结果能够解决问题(特别是基于异常消息)。我想我也是这样做的。如果我指定类型(string、int、long),而不是将TProperty设置为泛型,它似乎可以工作。我认为这是其中一种情况,我必须为每种值类型建立一个。嘿,这对你来说很好。谢谢你回来接受我的提问。我讨厌开放式的问题…第二部分看起来很好。。。我很想测试一下。第二部分是经过讨论后我知道你想要的。第一部分只是让其他读者在用好东西攻击他们之前,先用一个更简单的概念。
_models.Any(m => propertySelector(m) == propertySeletor(model));
// Expression<Func<TModel, TProperty>> propertySelector
// TModel model
var parameter = propertySelector.Parameters[0];
var left = propertySelector.Body;
var right = Expression.Invoke(propertySelector, Expression.Constant(model));
var body = Expression.Equal(left, right);
var predicate = Expression.Lambda<Func<TModel, bool>>(body, parameter);
var right = propertySelector.Body.ReplaceParameter(
    propertySelector.Parameters[0],
    Expression.Constant(model));
public static partial class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
        => new ParameterReplacer { Source = source, Target = target }.Visit(expression);

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