Posso estrarre e riutilizzare l'intera espressione in questo modo:
Expression<Func<User, int>> userExpression = x => x.Roles.Count()
ma è possibile estrarre alcuni come solo x.Roles.Count()
parte e usarlo nel contesto di Expression<Func<User, T>>
Cosa sto cercando di ottenere è riutilizzare quella parte attraverso differenti selezioni come:
users.Select(x => new AnotherClass { RoleCount = roleCountPartOfExpression})
e
users.Select(x => new OneMoreAnotherClass
{
AnotherProperty = roleCountPartOfExpression
});
Quindi, cosa dovrebbe essere in questo caso roleCountPartOfExpression
in questo caso deve essere supportato in LINQ alle entità (creando così un metodo in cui passerò User dove verrà return user.Roles.Count()
non funzionerà) inoltre non posso creare espressioni per selezionare come Expression<Func<User, AnotherClass>>
perché in quel caso dovrò creare Expression<Func<User, OneMoreAnotherClass>>
e ciò interromperà il mio obiettivo di "riutilizzabilità".
Se compili su Func<User, int>
, puoi chiamarlo in altre aree in questo modo:
Expression<Func<User, int>> userExpression = x => x.Roles.Count();
Func<User,int> userFunc = userExpression.Compile();
users.Select(x => new AnotherClass { RoleCount = userFunc(x) });
O semplicemente definisci come Func
per iniziare:
Func<User,int> userFunc = x => x.Roles.Count();
Sta usando Linq-to-Objects o qualcos'altro? Se devi mantenerlo come Expression
perché Expression
viene convertita in qualcos'altro (come una chiamata SQL), puoi utilizzare AsExpandable di AsExpandable
modo:
public static Expression<Func<User,int>> RoleCount()
{
return u => u.Roles.Count();
}
public static void DoStuff()
{
var roleCounter = RoleCount();
var query = users.AsExpandable()
.Select(u => roleCounter.Invoke(u));
}
Possiamo creare un metodo Combine
grado di prendere un selettore per un oggetto e poi un altro selettore che prende anche l'output del primo selettore per produrre un risultato finale:
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
Questo utilizza il seguente metodo di supporto per sostituire tutte le istanze di un'espressione con un'altra:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
Ora possiamo scrivere:
Expression<Func<User, int>> userExpression = x => x.Roles.Count()
var query = users.Select(userExpression.Combine((x, count) =>
new OneMoreAnotherClass { AnotherProperty = count});