And*_*ykh 8 .net c# linq entity-framework
假设我有这样的函数:
var filterValue = GetCurrentFilter(state);
Run Code Online (Sandbox Code Playgroud)
然后是EF查询:
var result = context.EntitySet.Where(x=> x.column > filterValue);
Run Code Online (Sandbox Code Playgroud)
这有效,但一旦我试图内联:
var result = context.EntitySet.Where(x=> x.column > GetCurrentFilter(state));
Run Code Online (Sandbox Code Playgroud)
它不是因为EF Linq试图解析GetCurrentFilter
到表达式树而无法做到这一点.这一切都是可以理解的.
我的问题是,有没有办法让EF Linq知道在GetCurrentFilter
构建树时需要执行函数并在树中使用它的结果?
就像是
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)));
Run Code Online (Sandbox Code Playgroud)
由于GetCurrentFilter没有参数作为查询的一部分,因此如果EF Linq可以支持它,那么技术上应该可以做到这一点.我怀疑我只是错过了正确的语法.
做GetCurrentFilter
一个(只读)属性,而不是方法.与方法不同,EF会将属性评估为其值,而不是尝试将它们转换为SQL.
您拥有的唯一其他方法是遍历整个表达式树,搜索ResultOf
方法的用法,将其参数计算为值,然后内联该值为ResultOf
调用值的值,重新围绕该值重新构建查询.
为了实现这一点,它意味着您不仅需要在调用中包装您想要内联的代码EfUtil.ResultOf
,而且还意味着调用查询本身的方法以强制它返回并评估它:
public class EfUtil
{
public static T ResultOf<T>(T value)
{
return value;
}
}
//Note this could probably use a better name
public static IQueryable<T> EvaluateResults<T>(this IQueryable<T> query)
{
return query.Provider.CreateQuery<T>(
new ExpressionEvaluator().Visit(query.Expression));
}
internal class ExpressionEvaluator : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.Name == "ResultOf" && m.Method.DeclaringType == typeof(EfUtil))
{
Expression target = m.Arguments[0];
object result = Expression.Lambda(target)
.Compile()
.DynamicInvoke();
return Expression.Constant(result, target.Type);
}
else
return base.VisitMethodCall(m);
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许你写:
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)))
.EvaluateResults();
Run Code Online (Sandbox Code Playgroud)
然后,它将GetCurrentFilter(state)
在客户端进行评估,并将结果内联为查询中的常量.
作为一个稍微简单的测试,我们可以编写以下内容:
var query = new[] { 1, 2, 3 }
.AsQueryable()
.Where(x => x > EfUtil.ResultOf(Math.Max(1, 2)))
.EvaluateResults();
Console.WriteLine(query.ToString());
Run Code Online (Sandbox Code Playgroud)
它会打印出来:
System.Int32 [].其中(x =>(x> 2))
这正是我们想要的.
请注意,lambda的参数(x
在这些示例中)的使用不能在调用中的任何地方使用,EfUtil.ResultOf
否则代码将无法工作,并且无法使其工作(尽管我们可以生成更好的错误消息,如果我们关心的话足够).