Is there a way to embed external functions in an EF Linq query?

Say I have a function like this:

var filterValue = GetCurrentFilter(state); 

And then the EF request:

 var result = context.EntitySet.Where(x=> x.column > filterValue); 

this works, but as soon as I try to enable this:

 var result = context.EntitySet.Where(x=> x.column > GetCurrentFilter(state)); 

This is not because EF Linq tried to parse GetCurrentFilter in the expression tree and cannot do this. All this is quite understandable.

My question is, is there a way to let EF Linq know that when executing the GetCurrentFilter function when assembling the tree and using its result in the tree?

Something like

 var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state))); 

Since GetCurrentFilter does not have parameters that are part of the request, this should be technically possible if EF Linq can support it. I suspect that the correct syntax is simply missing for this.

+7
c # linq entity-framework
source share
1 answer

Make the method GetCurrentFilter a (read-only) instead of the method. EF will evaluate properties by their values, and not try to translate them into SQL, unlike methods.


The only other way that you have is to go through the entire expression tree, look for the use of your ResultOf method, evaluate its parameter to a value, and then embed that value when the ResultOf call was once, retraining the query around that value .

For this to work, this means that you not only need to wrap the code you want to embed in the EfUtil.ResultOf call, but also means calling the method in the request itself to make it return and evaluate this:

 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); } } 

This will allow you to write:

 var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state))) .EvaluateResults(); 

He would then evaluate GetCurrentFilter(state) on the client side and inject the result as a constant into the request.

As a slightly simpler test, we can write the following:

 var query = new[] { 1, 2, 3 } .AsQueryable() .Where(x => x > EfUtil.ResultOf(Math.Max(1, 2))) .EvaluateResults(); Console.WriteLine(query.ToString()); 

And he will print:

System.Int32 []. Where (x => (x> 2))

This is exactly what we want.

Note that using the lambda parameter ( x in these examples) cannot be used anywhere in the call to EfUtil.ResultOf , or the code will not work and cannot be executed to work (although we can generate a better error message if we care enough )

+7
source share

All Articles