Si prega di considerare questo codice:
System.Linq.Expressions.Expression<Func<tbl, bool>> exp_details = r => r.ID_Master == Id &&
r.Year == Year &&
r.Month == Month ;
Voglio scrivere una funzione che si aspetta qualche argomento, e quindi recuperare alcuni dati dal mio database. Il problema è che voglio creare una condizione dinamica. Ad esempio se passo IsDeleted
argomento IsDeleted
con il valore true
, voglio aggiungere r.IsDeleted == true;
per exp_details
. Come posso fare questo?
La chiave per farlo è usare un ExpressionVisitor
che percorre l'espressione data e (opzionalmente, in una sottoclasse) consente di sostituire gli elementi riconosciuti con altri specificati dall'utente. Nel mio caso questo è stato fatto per ORM (NHibernate). Questo è quello che uso: (Aggiungerò i riferimenti alla mia risposta più tardi)
public class ParameterAssignAndReplacer : ExpressionVisitor
{
private readonly ParameterExpression _source;
private readonly ConstantExpression _target;
internal ParameterAssignAndReplacer(ParameterExpression source, ConstantExpression target)
{
_source = source;
_target = target;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Name == _source.Name ?
base.VisitConstant(_target) :
base.VisitParameter(node);
}
}
E..
public static class ExpressionExtensions
{
public static Expression<Func<TArg2, TResult>> AssignAndReduce<TArg1, TArg2, TResult>(
this Expression<Func<TArg1, TArg2, TResult>> func,
TArg1 parameter)
{
var exprBody = func.Body;
var assignedParameter = Expression.Constant(parameter, typeof(TArg1));
exprBody = new ParameterAssignAndReplacer(func.Parameters[0], assignedParameter).Visit(exprBody);
return Expression.Lambda<Func<TArg2, TResult>>(exprBody, func.Parameters[1]);
}
}
Entrambe le classi possono essere estese al tuo scenario specifico. Per renderlo pertinente al codice che hai postato (sto usando solo IsDeleted
come parametro per semplicità):
public class SomeClass
{
Expression<Func<tbl, bool, bool>> _templateExpression =
(tbl r, bool isDeleted) => r.ID_Master == 5 && r.Year == 2008 && r.Month == 12 && r.IsDeleted == isDeleted;
public Expression<Func<tbl, bool>> Foo(bool IsDeleted)
{
return _templateExpression.AssignAndReduce(IsDeleted);
}
}
Per quanto riguarda i riferimenti, la maggior parte di ciò che ho imparato su questo argomento proviene dalle risposte "Espressioni" di Marc Gravell [anche se molti altri utenti mi hanno aiutato a capire meglio questo :-)]
non è possibile utilizzare il corpo dell'istruzione in Expression Linq considerare invece l'uso di Predicato
var exp_details = new Predicate<tbl>(r =>
{
bool result == Id && r.Year == Year && r.Month == Month;
if(IsDeleted != null)
{
result &= r.IsDeleted == IsDeleted;
}
return result;
});
la massima espressione di Linq Func <T, bool> può essere sostituita con Predicate <T> .
IQueryable<tbl> query = ent.tbl.Where(r => r.ID_Master == Id && r.Year == Year);
//customize query
if(IsDeleted != null){
query = query.Where(r => r.IsDeleted == IsDeleted);
}
//execute the final generated query
var result = query.FirstOrDefault();
questo creerà la causa da IQueryable<T>
. Linq è abbastanza intelligente per query complesse.