Marca GetCurrentFilter
una proprietà (di sola lettura) anziché un metodo. EF valuterà le proprietà ai loro valori, piuttosto che provare a tradurli in SQL, a differenza dei metodi.
L'unica altra strada che avete è quello di attraversare l'intero albero di espressione, la ricerca per l'utilizzo del metodo ResultOf
, valutare la sua parametro su un valore, e quindi in linea, che il valore in cui la chiamata ResultOf
era una volta, rebuiding la query attorno a quel valore.
Per far funzionare ciò significa che è necessario non solo racchiudere il codice che si desidera in linea in una chiamata a EfUtil.ResultOf
, ma significa anche chiamare un metodo sulla query stessa per forzarlo a tornare indietro e valutarlo :
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);
}
}
Questo permetterebbe di scrivere:
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)))
.EvaluateResults();
sarebbe quindi valutare GetCurrentFilter(state)
sul lato client e inline il risultato come una costante nella query.
Come test leggermente più semplice, possiamo scrivere la seguente:
var query = new[] { 1, 2, 3 }
.AsQueryable()
.Where(x => x > EfUtil.ResultOf(Math.Max(1, 2)))
.EvaluateResults();
Console.WriteLine(query.ToString());
Ed stamperà:.
System.Int32 [] Dove (x => (x> 2))
Quale è esattamente ciò che vogliamo.
Si noti che l'uso del parametro lambda (x
in questi esempi) non può essere utilizzato in nessuna parte all'interno della chiamata a EfUtil.ResultOf
o il codice non funzionerà e non potrebbe essere fatto funzionare (sebbene potremmo generare un messaggio di errore migliore se ci tenevamo abbastanza).
Questo metodo potrebbe avere alcuni parametri, nessuno dei quali viene utilizzato nella query. Non c'è modo di definire le proprietà parametrizzate in C#. Ma tu hai ragione, nel senso che non l'ho chiarito nella mia domanda. Lascia che ti aggiorni. –
@zespri Questo problema è risolvibile, sebbene abbia una restrizione aggiuntiva. – Servy
La compilazione di una lambda per ogni query che viene emessa causerà un carico elevato della CPU. Quanto tempo impiega la compilazione di una lambda? 1ms? – usr