我想在执行之前重写LINQ表达式的某些部分.而且我在将重写器注入正确的位置时遇到了问题(实际上是这样).
查看实体框架源(在反射器中)它最终归结为IQueryProvider.ExecuteEF中的哪一个通过ObjectContext提供internal IQueryProvider Provider { get; }属性与表达式耦合.
所以我创建了一个包装器类(实现IQueryProvider),在调用Execute时执行Expression重写,然后将其传递给原始的Provider.
问题是,背后的领域Provider是private ObjectQueryProvider _queryProvider;.这ObjectQueryProvider 是一个内部密封类,这意味着不可能创建一个提供添加重写的子类.
因此,由于非常紧密耦合的ObjectContext,这种方法让我陷入了死胡同.
如何解决这个问题呢?我看错了方向吗?有没有办法让自己注意到这个ObjectQueryProvider?
更新:虽然提供的解决方案在您使用存储库模式"包装"ObjectContext时都能正常工作,但是允许直接使用ObjectContext生成的子类的解决方案将更可取.因此保持与Dynamic Data脚手架兼容.
为什么Func<>从Expression<Func<>>via .Compile()创建的文件比直接使用Func<>声明要慢得多?
我刚刚使用Func<IInterface, object>声明直接更改为Expression<Func<IInterface, object>>在我正在处理的应用程序中创建的一个,我注意到性能下降了.
我刚做了一点测试,Func<>从一个Expression创建的"几乎"是Func<>直接声明的时间的两倍.
在我的机器上,Direct Func<>大约需要7.5秒,Expression<Func<>>大约需要12.6秒.
这是我使用的测试代码(运行Net 4.0)
// Direct
Func<int, Foo> test1 = x => new Foo(x * 2);
int counter1 = 0;
Stopwatch s1 = new Stopwatch();
s1.Start();
for (int i = 0; i < 300000000; i++)
{
counter1 += test1(i).Value;
}
s1.Stop();
var result1 = s1.Elapsed;
// Expression . Compile()
Expression<Func<int, Foo>> expression = x => new Foo(x …Run Code Online (Sandbox Code Playgroud) 注意:我知道使用动态linq创建它很简单,但我想学习.
我想创建一个"找到"的lambda:Name = David AND Age = 10.
class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
var lambda = LabmdaExpression<Person>("Name", "David", "Age", 10);
static Expression<Func<T, bool>> LabmdaExpression<T>(string property1, string value1, string property2, int value2)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(Person), "o");
MemberExpression memberExpression1 = Expression.PropertyOrField(parameterExpression, property1);
MemberExpression memberExpression2 = Expression.PropertyOrField(parameterExpression, property2);
ConstantExpression valueExpression1 = Expression.Constant(value1, typeof(string));
ConstantExpression valueExpression2 = Expression.Constant(value2, typeof(int));
BinaryExpression binaryExpression1 = Expression.Equal(memberExpression1, valueExpression1);
BinaryExpression binaryExpression2 = Expression.Equal(memberExpression2, valueExpression2); …Run Code Online (Sandbox Code Playgroud) 如何有效地缓存从表达式树编译的方法?
public void SomeToStringCalls()
{
ToString(i => (i + 1).ToString(), 1);
ToString(i => (i + 1).ToString(), 2);
ToString(i => (i + 2).ToString(), 3);
ToString(i => (i + 2).ToString(), 4);
}
private string ToString<T>(Expression<Func<T, string>> expression, T input)
{
var method = expression.Compile();
return method.Invoke(input);
}
Run Code Online (Sandbox Code Playgroud)
上面,每个调用都将重新编译每个表达式,即使某些表达式是相同的.我不能Dictionary<Expression<Func<T, string>>, Func<T, string>>()从表达式缓存已编译的方法,因为equals它将失败.
基本上NUnit,xUnit,MbUnit,MsTest等具有类似于以下的方法:
Assert.IsGreater(a,b)
//or, a little more discoverable
Assert.That(a, Is.GreaterThan(b))
Run Code Online (Sandbox Code Playgroud)
但是,内置的这种比较运算符数量有限; 并且他们不必要地复制语言运算符.当我想要任何稍微复杂的东西时,比如...
Assert.That(a.SequenceEquals(b))
Run Code Online (Sandbox Code Playgroud)
我经常要么继续挖掘手册以找到NUnit-speak中表达式的等价物,要么被迫回退到具有较少有用错误消息的普通布尔断言.
但是,C#与任意表达式很好地集成 - 因此应该可以使用具有以下签名的方法:
void That(Expression<Func<bool>> expr);
Run Code Online (Sandbox Code Playgroud)
这种方法既可用于执行测试(即验证断言),也可用于在测试失败时提供不太透明的诊断; 毕竟,表达式可以呈现为伪代码,以指示哪个表达式失败; 通过一些努力,您甚至可以智能地评估失败的表达式,以提供子表达式值的一些线索.
例如:
Assert.That(()=> a == b);//could inspect expression and print a and b
Assert.That(()=> a < b && b < c);
//could mention the values of "a<b" and "b<c" and/or list the values of a, b, and c.
Run Code Online (Sandbox Code Playgroud)
至少,它会使表达式的并行语言变得不必要,并且在某些情况下,它可能使失败消息更有用.
这样的事情存在吗?
编辑: 在尝试(并且喜欢!)Power Assert之后,我最终重新实现它以解决一些限制.我的变体发表为ExpressionToCode ; 请参阅下面的答案以获取改进列表.
假设我有一个班级:
class MyClass {
public int MyMethod(Func<int, int> f) { return 0; }
public int MyMethod(Expression<Func<int, int>> f) { return 1; }
}
Run Code Online (Sandbox Code Playgroud)
当我尝试使用lambda表达式调用该方法时,我收到一个编译错误,指出调用在两个重载之间是不明确的:
var myClass = new MyClass();
myClass.MyMethod(x => 1 + x); // Error!
Run Code Online (Sandbox Code Playgroud)
当然,使用显式类型调用可以正常工作:
myClass.MyMethod((Func<int, int>)(x => 1 + x)); // OK, returns 0
myClass.MyMethod((Expression<Func<int, int>>)(x => 1 + x)); // OK, returns 1
Run Code Online (Sandbox Code Playgroud)
表达式树包含更多信息(实际代码),我可能希望在可用时使用此信息.但我也希望我的代码能够与代表合作.不幸的是,这种模糊性使得我必须找到另一种方法来区分两个调用,这会混淆一个干净的API.
C#规范没有说明这个具体情况,所以在这个意义上,行为确实符合规范.
但是,有一个论点要求表达式树应该优先于委托.该Compile方法充当从表达式树到委托的显式转换.表达式树包含更多信息,当您编译为委托时,您将丢失该信息.另一方面没有转换.
有没有理由不喜欢表达树?
我一直在理解PredicateBuilderJoseph Albahari编写的扩展方法,我看到了这一点Expression.Invoke,说实话,我无法理解它的原因在以下方法中:
public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>>
expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2,
expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
Run Code Online (Sandbox Code Playgroud)
即使他解释了一下:
有趣的工作发生在And和Or方法中.我们首先使用第一个表达式的参数调用第二个表达式.Invoke表达式使用给定表达式作为参数调用另一个lambda表达式.我们可以从第一个表达式的主体和第二个表达式的调用版本创建条件表达式.最后一步是将它包装在一个新的lambda表达式中.
MSDN告诉我:
创建一个InvocationExpression,将委托或lambda表达式应用于参数表达式列表.
这对我来说很有意义.所以基本上我不必传入任何参数,如果我使用这样的表达式.
但由于某种原因,我无法理解它.也许我累了什么的.
问题:
InvocationExpression.Or<T>方法(或AndElse<T>)方法如何更好地工作?更新:
我在考虑InvocationExpression当我从工作到家时,它在我心中暗示如下:
当我们调用一个方法时,我们简单地说CallMe(phoneNumber, time);,这称为方法调用.然后,InvocationExpression应该是一个表达的表达CallMe(phoneNumber, time);.它类似于LambdaExpression表达lambda等t => t + 2.所以基本上它是一个应用于参数(而不是参数)的方法调用.因此,作为调用,不再需要参数,但可能会返回一些内容,因为参数已经应用于其参数.
有关我正在讨论的代码的更多信息,请访问http://www.albahari.com/nutshell/predicatebuilder.aspx
我目前的理解是这样的'硬编码'代码:
public int Add(int x, int y) {return x + y;}
Run Code Online (Sandbox Code Playgroud)
将始终比表达式树代码执行更好,如下所示:
Expression<Func<int, int, int>> add = (x, y) => x + y;
var result = add.Compile()(2, 3);
var x = Expression.Parameter(typeof(int));
var y = Expression.Parameter(typeof(int));
return (Expression.Lambda(Expression.Add(x, y), x, y).
Compile() as Func<int, int, int>)(2, 3);
Run Code Online (Sandbox Code Playgroud)
因为编译器有更多信息,如果在编译时编译它,可以花更多精力优化代码.这一般是正确的吗?
假设我有一个表达式,它只是一个成员访问运算符链:
Expression<Func<Tx, Tbaz>> e = x => x.foo.bar.baz;
Run Code Online (Sandbox Code Playgroud)
您可以将此表达式视为子表达式的组合,每个子表达式包含一个成员访问操作:
Expression<Func<Tx, Tfoo>> e1 = (Tx x) => x.foo;
Expression<Func<Tfoo, Tbar>> e2 = (Tfoo foo) => foo.bar;
Expression<Func<Tbar, Tbaz>> e3 = (Tbar bar) => bar.baz;
Run Code Online (Sandbox Code Playgroud)
我想要做的是分解e成这些组件子表达式,以便我可以单独使用它们.
如果我有表达x => x.foo.bar,我已经知道如何中断x => x.foo.如何拉出其他子表达式foo => foo.bar?
我试图在C#中模拟"提升"成员访问操作符,就像CoffeeScript的存在访问操作符一样?..Eric Lippert表示,类似的运营商被考虑用于C#,但没有预算来实施它.
如果这样的运算符存在于C#中,您可以执行以下操作:
value = target?.foo?.bar?.baz;
Run Code Online (Sandbox Code Playgroud)
如果target.foo.bar.baz链的任何部分结果为null,那么整个事情将评估为null,从而避免NullReferenceException.
我想要一个Lift可以模拟这种事情的扩展方法:
value = target.Lift(x => x.foo.bar.baz); //returns target.foo.bar.baz or null
Run Code Online (Sandbox Code Playgroud)
我正在使用c#进行分布式系统并遇到障碍.
我需要能够使用类型序列化Predicate
Predicate<ICollection<IEntity>> p = (entities => entities.OfType<Person>().Count() <= 3);
Run Code Online (Sandbox Code Playgroud)
我相信这在.net中是不可能的,所以我的问题是,是否存在任何可以解决这个问题的框架.
我已经尝试了几个框架,但是继续遇到他们无法序列化带有集合或列表的谓词的问题
希望有人知道解决方案.几个星期以来一直坚持这个问题......
expression-trees ×10
c# ×9
lambda ×4
linq ×3
.net ×2
delegates ×1
expression ×1
func ×1
lifting ×1
performance ×1
unit-testing ×1