我需要一种方法将选择器表达式转换为谓词表达式,以便将它与一些参数一起传递给查询提供程序。
private Expression<Func<T, TKey>> PrimaryKeySelector { get; set; }
private ISpecification<T> GetByPrimaryKeySpecification(TKey key) {
lambda = ... // some magic, also unicorns
return new ExpressionSpecification(lambda);
}
在第一次尝试中,我使用Expression.Equal
构建了谓词表达式,但是这会导致分析表达式的查询提供程序出现问题(例如NHibernate因未知Equal
语句而失败)。另外,我已经看到了创建新ParameterExpression
的解决方案,并通过调用它来使用选择器表达式的结果,但我觉得这很糟糕,因为1)性能和2)参数表达式已包含在选择器表达式中。
有什么建议吗?
答案 0 :(得分:2)
你在这里要做的就是用另一个表达一个表达式。使用函数执行此操作非常简单;使用表达式执行它需要更多的工作。
你可以做的是将参数的所有实例替换为你的第二个表达式,谓词,第一个函数的主体,选择器。
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
这依赖于以下方法将一个表达式的所有实例替换为另一个:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
最后一个缺失的难题是,您无法使用==
运算符来比较通用方法中的值,因为您无法确定该类型覆盖==
运算符;您需要手动构建该等式表达式:
public static Expression<Func<T, bool>> EqualsValue<T>(T value)
{
var param = Expression.Parameter(typeof(T));
var body = Expression.Equal(param, Expression.Constant(value));
return Expression.Lambda<Func<T, bool>>(body, param);
}
这允许你写:
return PrimaryKeySelector.Compose(EqualsValue(key));
关于您对表现的担忧,请不要担心。您构建像这样的表达式树所花费的时间将比您将要使用它们的网络请求慢很多个数量级。在表达式操作上花一点时间,甚至不会对应用程序的性能产生可测量的影响;它将远低于网络请求的 noise 。