2015-01-02 41 views
2

我想創建一個基於自己的id獲取EntityFramework對象而不傳入lambda表達式作爲參數的方法GetById()的通用方法。對於下面的代碼,實體T的類型爲Message,對GetById()已實現的類已知,並且具有屬性MessageId以及其他幾個屬性。 MessageId名稱已在下面的示例中進行了硬編碼,因爲這仍然是實驗性的 - 從T中提取id屬性名稱在以後很容易修復。使用Func構建LambdaExpression <,>其中returnType是IQueryable <T>

我一直在努力尋找一種方法來構建一個簡單的LambdaExpression,其中IQueryable<T>作爲參數類型,並希望有人會有這樣的線索可以完成。我想要IQueryable<T>的原因是因爲我的底層渠道工廠提供程序需要此更復雜的查詢。

在下面的代碼var exp = Expression.Lambda<...>行示出了我想與落得表達式函數類型定義,但行給出了異常:

不能用於返回類型System.Boolean的表達鍵入IQueryable的

這是因爲體內有Boolean類型,而我的表情參數queryParamtRetIQueryable<Message>類型。此外,如果我將主體類型更改爲IQueryable<Message>,我無法找到屬性MessageId,因爲該類型不再輸入T,而是Message,但輸入的類型爲IQueryable<T>

public T GetById(int id) 
{ 
    var queryParamLeft = Expression 
     .Parameter(typeof(System.Data.Entity.DbSet<T>), "o"); 
    var queryParamRet = Expression 
     .Parameter(typeof(IQueryable<T>), "o"); 
    var entityFrameworkType = Expression 
     .Parameter(typeof(T), "o"); 
    var queryProperty = Expression 
     .PropertyOrField(entityFrameworkType, "MessageId"); 
    var body = Expression 
     .Equal(queryProperty, Expression.Constant(id)); 
    var exp = Expression 
     .Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
      body, 
      queryParamRet); 

    var returnXml = DoWithChannel(channel 
         => channel.Load(serializer.Serialize(exp))); 
} 
+1

它看起來像你缺少的一步你表達式 - 如果它在c#中被編寫爲LINQ語句,它會是什麼樣子? 'dbSet.Where(o => o.MessageId == id)'也許? – Rhumborl

+1

您錯過了'Expression.Call(Queryable.Where,Expression.Lambda <...>)' – Aron

回答

1

TLDR:寫出你想創建一個表達式的代碼,然後故意創建的表達,將它們合併爲外表達開始之前與任何內部表達式。


如果你寫你預期的代碼作爲一個功能,它會是這個樣子

public static IQueryable<T> FilterADbSet(DbSet<T> dbSet) 
{ 
    return Queryable.Where<T>(dbSet, o => o.MessageId == 34); 
} 

DbSet<T>類型的輸入參數,IQueryable<T>類型的輸出,並呼籲Queryable.Where<T>與參數的dbSet變量和一個表達式。

從外部開始,您首先需要構建表達式以傳遞給where子句。你已經在你的代碼中完成了。

接下來,您需要爲where子句創建lambda表達式。

var whereClause = Expression.Equal(queryProperty, Expression.Constant(id)); 

var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType); 

接下來,如註釋所示,您需要使用Expression.Call來創建主體。

我的最終結果使您的代碼工作如下。

static Expression<Func<IQueryable<T>, IQueryable<T>>> WhereMethodExpression = v => v.Where(z => true); 
static MethodInfo WhereMethod = ((MethodCallExpression)WhereMethodExpression.Body).Method; 

public T GetById(int id) 
{ 
    var queryParamLeft = Expression 
     .Parameter(typeof(System.Data.Entity.DbSet<T>), "dbSet"); 

    var entityFrameworkType = Expression 
     .Parameter(typeof(T), "entity"); 

    var queryProperty = Expression 
     .PropertyOrField(entityFrameworkType, "MessageId"); 

    var whereClause = Expression 
     .Equal(queryProperty, Expression.Constant(id)); 

    var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType); 

    var body = Expression.Call(
     WhereMethod, 
     queryParamLeft, 
     whereClauseLambda 
     ); 

    var exp = Expression 
     .Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
      body, 
      queryParamLeft); 

    var returnXml = DoWithChannel(channel 
         => channel.Load(serializer.Serialize(exp))); 

} 
  1. 我使用的表達式來獲取Queryable.Where<T>
  2. 你的身體表達的MethodInfo對象中需要通過queryParamLeft。queryParamRet不需要
相關問題