2012-01-10 55 views
4

假設我們有兩個類構成表達式:選擇器+謂詞?

public class EntityA 
{ 
    public EntityB EntityB { get; set; } 
} 

public class EntityB 
{ 
    public string Name { get; set; } 
    public bool IsDeleted { get; set; } 
} 

兩個表達式選擇和謂詞

Expression<Func<EntityA, EntityB>> selector = c => c.EntityB; 
Expression<Func<EntityB, bool>> predicate = c => c.IsDeleted && c.Name == "AAA"; 

我需要寫一個返回組合表達式像

Expression<Func<TSource, bool>> Compose<TPropType>(Expression<Func<TSource, TPropType>> selector, Expression<Func<TPropType, bool>> predicator) 
{ 
    // Expression API ??? 
} 

在我的例子的方法結果應該是

Expression<Func<EntityA, bool>> exp = Compose(selector, predicate); 

什麼就相當於提前

Expression<Func<EntityA, bool>> exp = c => c.EntityB.IsDeleted && c.EntityB.Name == "AAA"; 

感謝。

+0

這最後'c.Name == 「AAA」'應該是'c.EntityB.Name == 「AAA」',對吧? – AakashM 2012-01-10 09:58:56

+0

是的,你是對的。糾正。 – qmicron 2012-01-10 11:04:52

回答

0

你可以嘗試以下方法:

static Expression<Func<TSource, bool>> Compose<TSource, TPropType>(
    Expression<Func<TSource, TPropType>> selector, 
    Expression<Func<TPropType, bool>> predicator) 
{ 
    ParameterExpression param = Expression.Parameter(typeof(TSource), "sourceObj"); 
    Expression invokedSelector = Expression.Invoke(selector, new Expression[] { param }); 
    Expression invokedPredicate = Expression.Invoke(predicator, new[] { invokedSelector }); 

    return Expression.Lambda<Func<TSource, bool>>(invokedPredicate, new[] { param }); 
} 

下面是如何使用它:

static void Main() 
{ 
    Expression<Func<EntityA, EntityB>> selector = c => c.EntityB; 
    Expression<Func<EntityB, bool>> predicate = c => c.IsDeleted && c.Name == "AAA"; 

    Expression<Func<EntityA, bool>> exp = Compose(selector, predicate); 
    System.Console.WriteLine(exp.Compile()(new EntityA())); 
} 
+0

謝謝伊戈爾,一切正常。 – qmicron 2012-01-10 20:30:44

2

調用這些lambda表達式肯定是你不想做的東西。你應該做的是重寫表達式。您只需要一種將值綁定到lambda表達式的方式,就像調用它們一樣。要做到這一點,請使用您綁定的值重寫表達式的主體替換參數。您可以使用此SubstitutionVisitor幫助做到這一點:

public class SubstitutionVisitor : ExpressionVisitor 
{ 
    public Expression OldExpr { get; set; } 
    public Expression NewExpr { get; set; } 

    public override Expression Visit(Expression node) 
    { 
     return (node == OldExpr) ? NewExpr : base.Visit(node); 
    } 
} 

鑑於這些表達式例如:

Expression<Func<EntityA, EntityB>> selector = 
    entityA => entityA.EntityB; 
Expression<Func<EntityB, bool>> predicate = 
    entityB => entityB.IsDeleted && entityB.Name == "AAA"; 

的目標是有效地改寫它,所以它變成這樣:

Expression<Func<EntityA, bool>> composed = 
    entity => entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA"; 
static Expression<Func<TSource, bool>> Compose<TSource, TProp>(
    Expression<Func<TSource, TProp>> selector, 
    Expression<Func<TProp, bool>> predicate) 
{ 
    var parameter = Expression.Parameter(typeof(TSource), "entity"); 
    var property = new SubstitutionVisitor 
    { 
     OldExpr = selector.Parameters.Single(), 
     NewExpr = parameter, 
    }.Visit(selector.Body); 
    var body = new SubstitutionVisitor 
    { 
     OldExpr = predicate.Parameters.Single(), 
     NewExpr = property, 
    }.Visit(predicate.Body); 
    return Expression.Lambda<Func<TSource, bool>>(body, parameter); 
} 

爲了理解這裏發生了什麼,他re是一行一行的解釋:

  1. 爲我們正在創建的新lambda創建一個新參數。

    entity => ... 
    
  2. 表示的選擇,我們的新參數entity替換原有的參數entityA的所有實例從拉姆達的身體獲得的財產。

    entityA => entityA.EntityB 
    // becomes 
    entity.EntityB 
    
  3. 鑑於謂詞,與以前取得的財產entity.EntityB替換原有的參數entityB的所有實例從拉姆達的身體,以獲得我們的新拉姆達的身體。

    entityB => entityB.IsDeleted && entityB.Name == "AAA" 
    // becomes 
    entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA" 
    
  4. 把它一起到新的拉姆達。

    entity => entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA" 
    
+0

另一個解決方案是使用LinqKit。在這種情況下, 表達式> output = c => predicate.Invoke(selector.Invoke(c)); – qmicron 2012-01-16 14:48:05

+0

我不會這麼說。它仍編譯並調用表達式。如果它將參數綁定到lambda參數,那麼是的,我認爲它是一個解決方案。這基本上是一個更好的方法來做另一個解決方案。我對這個庫不太熟悉,但它看起來像需要'展開()'表達式來獲得我的解決方案的結果。 – 2012-01-16 18:21:29