2013-05-29 20 views
13

Supponiamo che io abbia qualcosa di simileDa BinaryExpression a Expression <Func <T, bool>>

Expression<Func<SomeType, DateTime>> left = x => x.SomeDateProperty; 
Expression<Func<SomeType, DateTime>> right = x => dateTimeConstant; 
var binaryExpression = Expression.GreaterThan(left, right); 
Expression<Func<SomeType, bool>> predicate = 
          x => x.SomeDateProperty> dateTimeConstant; 

1) Come posso sostituire la mano destra di assegnazione dell'ultima riga con qualcosa che utilizza invece il binaryExpression? var predicate = x => binaryExpression; non funziona.

2) Il right è sempre una costante, non necessariamente DateTime.Now. Potrebbe essere di tipo più semplice Expression? Ad esempio, non dipende da SomeType, è solo una costante.

3) Se ho lo GreaterThan come string, c'è un modo per passare da questa stringa al metodo con lo stesso nome in Expression? In generale, se il nome del metodo di confronto è indicato come string, come posso passare dalla stringa per chiamare effettivamente il metodo con lo stesso nome sulla classe Expression?

Deve funzionare con LINQ alle Entità, se è importante.

+0

È possibile modificare gli alberi di espressione utilizzando la classe ExpressionVisitor. – Steven

risposta

13

1 e 2: è necessario costruire manualmente l'albero di espressione per fare questo, il compilatore non può aiutare, perché costruisce solo espressioni ready-made che rappresentano le funzioni nella loro interezza. Non è utile quando vuoi costruire le funzionalità pezzo per pezzo.

Ecco un modo semplice per costruire l'espressione che si desidera:

var argument = Expression.Parameter(typeof(SomeType)); 
var left = Expression.Property(argument, "SomeDateProperty"); 
var right = Expression.Constant(DateTime.Now); 

var predicate = Expression.Lambda<Func<SomeType, bool>>(
    Expression.GreaterThan(left, right), 
    new[] { argument } 
); 

si può prendere questo per un test drive con

var param = new SomeType { 
    SomeDateProperty = DateTime.Now.Add(TimeSpan.FromHours(-1)) 
}; 

Console.WriteLine(predicate.Compile()(param)); // "False" 

3: Dal momento che con ogni probabilità il numero di scelte possibili per il tuo predicato binario sarà abbastanza piccolo, puoi farlo con un dizionario:

var wordToExpression = 
    new Dictionary<string, Func<Expression, Expression, BinaryExpression>> 
{ 
    { "GreaterThan", Expression.GreaterThan }, 
    // etc 
}; 

Quindi, invece di codificare il codice Expression.GreaterThan nel primo frammento, farebbe qualcosa come wordToExpression["GreaterThan"](left, right).

Naturalmente questo può anche essere fatto in modo standard con la riflessione.

7

Quando si utilizza GreaterThan, è necessario specificare l'espressione corpi, non la lambda stessa. Sfortunatamente, c'è una complicazione: il x nelle due espressioni non è lo stesso.

In questo particolare caso, si potrebbe quasi farla franca con questo, perché la seconda espressione non usa x

Così; il tuo "1" e "2" dovrebbero essere risolte entro:

var binaryExpression = Expression.GreaterThan(left.Body, right.Body); 
    var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression, 
     left.Parameters); 

Tuttavia, per gestire questo nel caso generale, è necessario riscrivere su delle espressioni, per fissare-up i parametri:

var binaryExpression = Expression.GreaterThan(left.Body, 
    new SwapVisitor(right.Parameters[0], left.Parameters[0]).Visit(right.Body)); 
var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression, 
    left.Parameters); 

con:

public class SwapVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public SwapVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

Per la vostra "3"; non c'è nulla di costruito per quello; è possibile utilizzare la riflessione, tuttavia:

string method = "GreaterThan"; 

var op = typeof(Expression).GetMethod(method, 
    BindingFlags.Public | BindingFlags.Static, 
    null, new[] { typeof(Expression), typeof(Expression) }, null); 

var rightBody = new SwapVisitor(right.Parameters[0], 
    left.Parameters[0]).Visit(right.Body); 
var exp = (Expression)op.Invoke(null, new object[] { left.Body, rightBody }); 

var lambda = Expression.Lambda<Func<SomeType, bool>>(exp, left.Parameters); 
Problemi correlati