可重复使用的表达式
Reusable expression
给定一个带有 Where 子句的查询
CollectionA.Where(a => a.Prop1 == val1 && a.Prop2 == val2)
和另一个具有类似 Where 子句但属性通过引用链接的查询。
CollectionB.Where(b => b.Reference.Prop1 == val1 && b.Reference.Prop2 == val2)
对于这确实有效的函数:
Func<A, bool> f1 = a => a.Prop1 == val1 && a.Prop2 == val2;
Func<B, bool> g1 = b => f1(b.Reference);
对于表达式这不起作用:
Expression<Func<A, bool>> f2 = a => a.Prop1 == val1 && a.Prop2 == val2;
Expression<Func<B, bool>> g2 = b => f2(b.Reference); // <-- Method name expected.
我想使用规范在我的查询中重复使用该表达式。
像这样:
Specification specification = new Specification(val1, val2)
CollectionA.Where(specification.ToExpression());
CollectionB.Where(specification.ToExpression(x => x.Reference));:
public class Specification
{
private readonly int val1;
private readonly long val2;
public Specification(int val1, long val2)
{
this.val1 = val1;
this.val2 = val2;
}
public Expression<Func<A, bool>> ToExpression()
{
return x => x.Prop1 == val1 && x.Prop2 == val2;
}
public Expression<Func<B, bool>> ToExpression<B>(Expression<Func<B, A>> navigate)
{
// ?
}
}
如何实现这个方法?
此外,我希望它不仅适用于二进制 'and' 表达式,而且适用于任何表达式(即任何组合深度和参数类型)。 (例如 a => a.Prop1 == val1 && a.Prop2.Prop2a == val2a && a.Prop2.Prop2a == val2a
)但基本上它只是实现我尝试用上面的函数 g2 做的事情。
不能直接调用另一个表达式f2(b.Reference)
。创建一个编译和调用 f2
.
的表达式是徒劳的
您真正想要做的是编写表达式。创建一个新表达式,表示一个链接到另一个的表达式。您缺少的表达式实际上只是从 B
中获取 A
的参数选择器,如下所示:b => b.Reference;
这里有一个方便的 Compose
方法(类似于 )来帮助将它们链接在一起。
class A
{
public int Prop1 = 1;
public int Prop2 = 2;
}
class B
{
public A Reference;
}
class Program
{
static Expression<Func<A, C>> Compose<A, B, C>(
Expression<Func<A, B>> fAB, Expression<Func<B, C>> fBC)
{
var arg = Expression.Parameter(typeof(A));
return Expression.Lambda<Func<A, C>>(
Expression.Invoke(fBC, Expression.Invoke(fAB, arg)), arg);
}
static void Main(string[] args)
{
int val1 = 1;
int val2 = 2;
Func<A, bool> f1 = a => a.Prop1 == val1 && a.Prop2 == val2;
Func<B, bool> g1 = b => f1(b.Reference);
Expression<Func<A, bool>> f2 = a => a.Prop1 == val1 && a.Prop2 == val2;
Expression<Func<B, A>> argSelect = b => b.Reference;
var g2 = Compose<B, A, bool>(argSelect, f2);
A objA = new A();
B objB = new B() { Reference = objA };
var g2Compiled = g2.Compile();
Console.WriteLine(g2Compiled.Invoke(objB));
// Demonstrate that it's connected to our local variable
val2 = 3;
Console.WriteLine(g2Compiled.Invoke(objB));
}
}
给定一个带有 Where 子句的查询
CollectionA.Where(a => a.Prop1 == val1 && a.Prop2 == val2)
和另一个具有类似 Where 子句但属性通过引用链接的查询。
CollectionB.Where(b => b.Reference.Prop1 == val1 && b.Reference.Prop2 == val2)
对于这确实有效的函数:
Func<A, bool> f1 = a => a.Prop1 == val1 && a.Prop2 == val2;
Func<B, bool> g1 = b => f1(b.Reference);
对于表达式这不起作用:
Expression<Func<A, bool>> f2 = a => a.Prop1 == val1 && a.Prop2 == val2;
Expression<Func<B, bool>> g2 = b => f2(b.Reference); // <-- Method name expected.
我想使用规范在我的查询中重复使用该表达式。
像这样:
Specification specification = new Specification(val1, val2)
CollectionA.Where(specification.ToExpression());
CollectionB.Where(specification.ToExpression(x => x.Reference));:
public class Specification
{
private readonly int val1;
private readonly long val2;
public Specification(int val1, long val2)
{
this.val1 = val1;
this.val2 = val2;
}
public Expression<Func<A, bool>> ToExpression()
{
return x => x.Prop1 == val1 && x.Prop2 == val2;
}
public Expression<Func<B, bool>> ToExpression<B>(Expression<Func<B, A>> navigate)
{
// ?
}
}
如何实现这个方法?
此外,我希望它不仅适用于二进制 'and' 表达式,而且适用于任何表达式(即任何组合深度和参数类型)。 (例如 a => a.Prop1 == val1 && a.Prop2.Prop2a == val2a && a.Prop2.Prop2a == val2a
)但基本上它只是实现我尝试用上面的函数 g2 做的事情。
不能直接调用另一个表达式f2(b.Reference)
。创建一个编译和调用 f2
.
您真正想要做的是编写表达式。创建一个新表达式,表示一个链接到另一个的表达式。您缺少的表达式实际上只是从 B
中获取 A
的参数选择器,如下所示:b => b.Reference;
这里有一个方便的 Compose
方法(类似于
class A
{
public int Prop1 = 1;
public int Prop2 = 2;
}
class B
{
public A Reference;
}
class Program
{
static Expression<Func<A, C>> Compose<A, B, C>(
Expression<Func<A, B>> fAB, Expression<Func<B, C>> fBC)
{
var arg = Expression.Parameter(typeof(A));
return Expression.Lambda<Func<A, C>>(
Expression.Invoke(fBC, Expression.Invoke(fAB, arg)), arg);
}
static void Main(string[] args)
{
int val1 = 1;
int val2 = 2;
Func<A, bool> f1 = a => a.Prop1 == val1 && a.Prop2 == val2;
Func<B, bool> g1 = b => f1(b.Reference);
Expression<Func<A, bool>> f2 = a => a.Prop1 == val1 && a.Prop2 == val2;
Expression<Func<B, A>> argSelect = b => b.Reference;
var g2 = Compose<B, A, bool>(argSelect, f2);
A objA = new A();
B objB = new B() { Reference = objA };
var g2Compiled = g2.Compile();
Console.WriteLine(g2Compiled.Invoke(objB));
// Demonstrate that it's connected to our local variable
val2 = 3;
Console.WriteLine(g2Compiled.Invoke(objB));
}
}