cerco di interrogare (linq su entità EF Core) una raccolta di proprietà di navigazione, quindi uso any () come questo:
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == myvar );
Funziona perfettamente ma ora voglio costruire il predicato e non lo ho definito direttamente nella query. faccio così :
Func<T, bool> mypredicate = (p => p.myprop == myvar);
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(mypredicate);
(Ho sostituito T con il nome della mia entità)
ma questo genera un errore: L'oggetto di tipo 'System.Linq.Expressions.TypedParameterExpression' non può essere convertito in tipo 'System.Linq.Expressions.LambdaExpression'.
Come posso costruire il mio predicato per usarlo nella collezione Any ()? Grazie
Questa riga per esempio:
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == 1));
Quando compilato verrà compilato per qualcosa di simile:
var xParameter = Expression.Parameter(typeof(Entity1), "x");
var pParameter = Expression.Parameter(typeof(Entity2), "p");
var anyMethod =
typeof(Enumerable)
.GetMethods()
.Single(x => x.Name == "Any" && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(Entity2));
var anyCondition = Expression.Lambda<Func<Entity2, bool>>(
Expression.Equal(
Expression.Property(
pParameter,
typeof(Entity2).GetProperty("myprop").GetMethod),
Expression.Constant(1, typeof(int))),
pParameter);
var query = context.MyTable.Where(
Expression.Lambda<Func<Entity1, bool>>(
Expression.Call(
null,
anyMethod,
new Expression[] {
Expression.Property(
xParameter,
typeof(Entity1).GetProperty("mycollectionproperties").GetMethod),
anyCondition
}),
xParameter));
Questo è chiamato un albero di espressione. Vedi questo riferimento per maggiori dettagli: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/
Sebbene il metodo Any
Func
, durante la costruzione dell'albero delle espressioni, si noti che un'espressione ( Expression<Func<Entity2, bool>>
) viene assegnata al metodo Any
.
Non sembra esserci un modo da C # per dare al metodo Any
un'espressione invece di un Func
anche se l'intera cosa è un albero di espressioni (intendo in un modo parametrizzato come si desidera ottenere).
Il modo più ovvio per ottenere ciò che vuoi è utilizzare il codice di questo post e sostituire la variabile anyCondition
con qualsiasi espressione tu voglia utilizzare per la condizione in Any
.
Un altro modo è quello di costruire una parte dell'albero delle espressioni "normalmente" e passare null
al metodo Any
e quindi utilizzare un visitatore di espressioni per sostituire il null
con la propria espressione. Ecco come apparirà questo visitatore:
public class AnyMethodArgumentReplacingVisitor : ExpressionVisitor
{
private readonly Expression expression;
public AnyMethodArgumentReplacingVisitor(Expression expression)
{
this.expression = expression;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == "Any")
{
return Expression.Call(node.Object, node.Method, node.Arguments[0], expression);
}
return base.VisitMethodCall(node);
}
}
Ecco come lo useresti:
Expression<Func<Entity2, bool>> predicate =
a => a.myprop == 2;
Expression<Func<Entity1, bool>> expression =
b => b.mycollectionproperties.Any(null);
var expression2 =
(Expression<Func<Entity1, bool>>)
new AnyMethodArgumentReplacingVisitor(predicate).Visit(expression);
Si prega di notare che tale visitatore dovrebbe sostituire la chiamata a qualsiasi Any
metodo. Suppone anche che venga utilizzato solo il sovraccarico di Any
che richiede un predicato. C'è un altro sovraccarico di Any
che non richiede un predicato. Se è necessario utilizzarlo, è necessario modificare il codice.