扩展访问表达式以检查值 [英] Extending an access expression to check for value
问题描述
我目前正试图与表达式树搏斗,以使魔术发生,但我不断出错.
I'm currently trying to wrestle with expression trees to make a bit of magic happen, but I keep hitting error after error.
我的某些域对象(实体框架)上有一些这样的属性
I've got some properties like this on some of my domain objects (Entity Framework)
Expression<Func<DomainObject, LinkedDomainObject>> IncludeExpr
{
get {
return o => o.SomeLinkedObject;
}
}
和另一个用于检查链接对象在某些属性(例如ID)上是否相等的表达式.
and another expression that checks that linked object for equality on some property (e.g. ID).
我确实有一个表达式,该表达式还检查了链接对象是否为空,这样我可以通过反转空检查表达式来组成一个 NotNull
和 Matched ID
表达式并将其通过 AndAlso
与ID检查表达式进行组合.
I did have an expression that also checked that linked object for being null, and that way I could compose a NotNull
and Matched ID
expression by inverting the null check expression and combining it via AndAlso
with the ID check expression.
我想使用 o =>o.SomeLinkedObject
表达式和 linkedObject =>linkedObject.ID == idVar
表达式并将它们混在一起以有效地获得:
I want to take the o => o.SomeLinkedObject
expression and linkedObject => linkedObject.ID == idVar
expressions and mash them together to effectively get:
o =>o.LinkedObject!= null&&o.LinkedObject.Id == idVar
但是我无法终生确定如何基于这两个单独的表达式将表达式树组合在一起.
But I can't for the life of me work out how I'd get an expression tree together based on those two separate expressions.
推荐答案
我们可以花一些时间来创建一个帮助程序方法,该方法可以使解决此问题变得非常简单.如果我们创建一个使我们能够像编写委托一样容易地编写表达式的方法,这将变得非常容易.我们的 Compose
方法将接受一个表达式,另一个将获取第一个输出并将其转换为其他内容的表达式,从而创建一个新表达式,该表达式可将第一个输入的类型转换为一个表达式.第二个输出:
We can take a moment to create a helper method that can make solving this problem very straightforward. If we create a method that lets us compose expressions as easily as we can compose delegates, this becomes very easy. Our Compose
method will accept an expression, and another that takes the output of the first and transforms it into something else, creating a new expression that can transform something of the type of the input of the first into the output of the second:
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);
}
这依赖于以下方法将一个表达式的所有实例替换为另一个表达式:
This is dependent on the following method to replace all instances of one expression with another:
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);
}
}
现在,我们可以非常轻松地创建一个 IsNotNull
转换:
Now we can create an IsNotNull
transformation very easily:
public static Expression<Func<TSource, bool>> IsNotNull<TSource, TKey>(
this Expression<Func<TSource, TKey>> expression)
{
return expression.Compose(key => key != null);
}
对于和
,将两个表达式组合在一起,如果使用LINQ查询提供程序,最简单的选择是仅对每个表达式分别调用 Where
,如果可以的话.如果没有,则可以使用 PrediacteBuilder
将两个表达式一起 And
或 Or
:
As for And
-ing two expressions together, the easiest option if using a LINQ query provider is to just call Where
on each expression separately, if that's an option. If not, you can use a PrediacteBuilder
to And
or Or
two expressions together:
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}
这篇关于扩展访问表达式以检查值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!