2010-04-28 53 views
8

我有一個類,用戶Linq to SQL DynamicInvoke(System.Object [])'不支持轉換爲SQL

用戶有一個用戶ID財產。

我有一個看起來像這樣的方法:

static IQueryable<User> FilterById(this IQueryable<User> p, Func<int, bool> sel) 
{ 
    return p.Where(m => sel(m.UserId)); 
} 

不可避免的是,當我打電話的功能:

var users = Users.FilterById(m => m > 10); 

我得到以下異常:

方法'System.Object DynamicInvoke(System.Object [])'沒有 支持翻譯爲SQ L.

有沒有解決這個問題的方法? Expression.KillMeAndMyFamily()的兔子洞可能要走多遠?

澄清爲什麼我這樣做:我使用T4模板自動生成一個簡單的存儲庫和一個管道系統。管道,而不是寫在:

new UserPipe().Where(m => m.UserId > 10 && m.UserName.Contains("oo") && m.LastName == "Wee"); 

我想產生類似:

new UserPipe() 
    .UserId(m => m > 10) 
    .UserName(m => m.Contains("oo")) 
    .LastName("Wee"); 
+6

+1 for Expression.KillMeAndMyFamily() – BenDundee 2013-01-19 00:30:13

回答

15

讓我們UserId作爲一個例子。你想寫:

new UserPipe().UserId(uid => uid > 10); 

,並希望這是一樣的:

new UserPipe().Where(user => user.UserID > 10); 

你需要做的是採取的第一個版本的表達式樹,並將其轉換爲第二個版本。


所以,首先改變UserId簽名以接受一個表達式樹,而不是編譯拉姆達:

public static IQueryable<User> UserId(
    IQueryable<User> source, Expression<Func<int, bool>> predicate) 

然後,寫,所述第一表達式樹轉換爲第二方法版。讓我們來看看這兩個表達式樹:

輸入:

 
     Lambda 
     uid 
      | 
     BinaryOp 
      > 
    /   \ 
Parameter  Constant 
    uid   10 

輸出:

 
     Lambda 
      user 
      | 
     BinaryOp 
      > 
    /   \ 
Property   Constant 
    UserID    10 
    | 
Parameter 
    user 

正如你所看到的,所有你需要做的就是把屍體的lambda遞歸地替換參數uid與參數user上的屬性UserId的所有匹配項,並創建一個新的lamb da表達與轉換後的主體和參數user

您可以使用ExpressionVisitor進行更換。

0

乍一看,你看起來像是在建立一些Linq不會知道如何轉換成T-SQL的表達式。

我可能會誤解你想要做什麼,但是如果你想構建Linq To Sql可以理解的連鎖表達式,我強烈建議查看PredicateBuilder擴展here。即使這不完全是你想要的,但瞭解它的工作原理可以讓你瞭解你需要在封面上實現什麼,以使你在做什麼。

1

由於DTB,這裏就是我想出了:

public class ExpressionMemberMerger : ExpressionVisitor 
{ 
    MemberExpression mem; 
    ParameterExpression paramToReplace; 

    public Expression Visit<TMember, TParamType>(
     Expression<Func<TParamType, bool>> exp, 
     Expression<Func<TMember, TParamType>> mem) 
    { 
     //get member expression 
     this.mem = (MemberExpression)mem.Body; 

     //get parameter in exp to replace 
     paramToReplace = exp.Parameters[0]; 

     //replace TParamType with TMember.Param 
     var newExpressionBody = Visit(exp.Body); 

     //create lambda 
     return Expression.Lambda(newExpressionBody, mem.Parameters[0]); 
    } 

    protected override Expression VisitParameter(ParameterExpression p) 
    { 
     if (p == paramToReplace) return mem; 
     else return base.VisitParameter(p); 
    } 
} 

現在,我可以轉換謂詞沒有記錯,用類似下面的代碼。我對這段代碼做了一些測試;它似乎在工作,但我有興趣聽到任何意見/問題:

static IQueryable<User> FilterById(this IQueryable<User> p, Expression<Func<int, bool>> sel) 
{ 
    var merger = new ExpressionMemberMerger(); 

    Expression<Func<User, int>> mem = m => m.UserId; 

    var expression = (Expression<Func<User, bool>>)merger.Visit(sel, mem); 

    return p.Where(expression); 
}