2009-10-09 64 views
3

我想鏈接多個編譯LINQ查詢在一起。我成功地將兩個查詢鏈接在一起,但我無法獲得三個正確的工作鏈。所以這裏是減少我的代碼重新創建問題。我的兩個問題是:'爲什麼這不起作用?'和'是否有更好的方法來保持已編譯查詢的性能優勢,並避免重複使用常用的基本查詢邏輯?可以將多個已編譯的linq查詢鏈接在一起嗎?

定義以下兩個查詢:

Func<DataContext, IQueryable<User>> selectUsers = 
    CompiledQuery.Compile(
     (DataContext dc)=>dc.Users.Select(x=>x) 
    ); 
//   
Func<DataContext, string, IQueryable<User>> filterUserName = 
    CompiledQuery.Compile(
     (DataContext dc, string name) => 
      selectUsers(dc).Where(user=>user.Name == name) 
    ); 

通話和枚舉鏈正常工作:

filterUserName(new DataContext(), "Otter").ToList(); 

添加第三個查詢鏈:

Func<DataContext, string, int, IQueryable<User>> filterUserAndGroup =  
    CompiledQuery.Compile(
     (DataContext dc, string name, int groupId) => 
      filterUserName(dc, name).Where(user=>user.GroupId == groupId) 
    ); 

調用鏈不起作用:

filterUserAndGroup(new DataContext(), "Otter", 101); 

System.InvalidOperationException: 成員訪問 '字符串名稱' '用戶' 上 類型不合法的「System.Linq.IQueryable 1[User].. at System.Data.Linq.SqlClient.SqlMember.set_Expression(SqlExpression value) at System.Data.Linq.SqlClient.SqlFactory.Member(SqlExpression expr, MemberInfo member) at System.Data.Linq.SqlClient.SqlBinder.Visitor.AccessMember(SqlMember m, SqlExpression expo) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitMember(SqlMember m) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitBinaryOperator(SqlBinary bo) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias a) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource source) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitIncludeScope(SqlIncludeScope scope) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Bind(SqlNode node) at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection 1個 parentParameters,SqlNodeAnnotations 註釋)在 將System.Data.Linq .SqlClient.SqlProvider.BuildQuery(表達式 查詢,SqlNodeAnnotations註解) 在 System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Compile(表達式 查詢)在 將System.Data.Linq .CompiledQuery.ExecuteQuery(DataContext 上下文在TestMethod的()中 ,對象[]參數)在 System.Data.Linq.CompiledQuery.Invoke(TArg0 爲arg0,TArg1 ARG1)....

+0

隨時恢復它,但我可以忍受不得不滾動一行。 – 2009-10-09 14:24:40

回答

3

看起來您需要在執行第二個查詢之前將您的第一個已編譯查詢轉換爲列表。在理論上,這也應該導致你的兩個查詢鏈出現錯誤。

MSDN CompiledQuery

如果一個新的查詢運算符應用於委託執行的結果,產生一個異常。

當您想要對執行編譯查詢的結果執行查詢操作符時,必須先將結果轉換爲列表,然後再對其進行操作。

也許這段代碼可以修復它,儘管如果你使用LINQ to SQL,這可能會影響到往返數據庫。

filterUserName(dc, name).ToList().Where(user=>user.GroupId == groupId) 
+0

AsEnumerable()就足夠了,不需要調用ToList()...除了保存大量不需要的內存複製,如果你使用AsEnumerable()而不是使用ToList() – 2009-10-09 15:09:58

+0

哇,我甚至沒有考慮過可能存在L2S允許構建,編譯和執行甚至不支持的查詢的錯誤。這讓我很震驚,因爲鏈接兩個已編譯的查詢實際上會返回正確的結果。我想接受這個答案,但是我想知道它究竟是如何起作用的? – 2009-10-09 16:21:29

+1

如果使用AsEnumerable()或.ToList(),它將不會動態構建SELECT語句。它會將數據放在第一個.ToList()上,然後在內存中而不是在服務器上執行查詢的請求。 – 2009-10-09 17:10:56

0

誠然,我不熟悉CompiledQuery。但是,由於LINQ的延期執行性質,你可以這樣做:

var result = dbContext.Users.Where(user => user.id == id); 
result = result.Where(user => user.GroupID == groupID); 
result = result.Select(user => user.username); 

for(String username in result){ 
    ; // do something 
} 

上面當然是一個簡單的例子。但是,當基於用戶輸入(例如網站上的「高級搜索」表單)將不同查詢組合在一起時,它可能非常有用。

+0

= = 你忘了這些 – 2009-10-12 08:56:58

+0

糟糕!謝謝... – Matt 2009-10-12 12:48:54

0

您是否需要使用CompiledQuery類?試試這個...

static Func<DataContext, IQueryable<User>> selectUsers = 
    (dc) => dc.Users.Select(x => x); 
//   
static Func<DataContext, string, IQueryable<User>> filterUserName = 
    (DataContext dc, string name) => 
     selectUsers(dc).Where(user => user.Name == name); 
// 
static Func<DataContext, string, int, IQueryable<User>> filterUserAndGroup = 
    (DataContext dc, string name, int groupId) => 
     filterUserName(dc, name).Where(u => u.GroupID == groupId); 

...測試代碼(我知道我的DataContext這裏就不LINQ2SQL但這是樂趣和LINQ的美)......

另外,我用這個方法對我自己的數據庫,所以我知道他們建立到單個查詢發送到數據庫。我甚至使用了返回IQueryable而不是Func <>代表的普通實例方法。

public class DataContext 
{ 
    public static Func<DataContext, IQueryable<User>> selectUsers = 
     (dc) => dc.Users.Select(x => x); 
    //   
    public static Func<DataContext, string, IQueryable<User>> filterUserName = 
     (DataContext dc, string name) => 
      selectUsers(dc).Where(user => user.Name == name); 
    // 
    public static Func<DataContext, string, int, IQueryable<User>> UsrAndGrp = 
     (DataContext dc, string name, int groupId) => 
      filterUserName(dc, name).Where(u => u.GroupID == groupId); 

    public DataContext() 
    { 
     Users = new List<User>() 
     { 
      new User(){ Name = "Matt", GroupID = 1}, 
      new User(){ Name = "Matt", GroupID = 2}, 
      new User(){ Name = "Jim", GroupID = 2}, 
      new User(){ Name = "Greg", GroupID = 2} 
     }.AsQueryable(); 
    } 
    public IQueryable<User> Users { get; set; } 
    public class User 
    { 
     public string Name { get; set; } 
     public int GroupID { get; set; } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var q1 = DataContext.UsrAndGrp(new DataContext(), "Matt", 1); 
     Console.WriteLine(q1.Count()); // 1 
     var q2 = DataContext.filterUserName(new DataContext(), "Matt"); 
     Console.WriteLine(q2.Count()); // 2 
    } 
} 
+0

由於性能要求,使用已編譯的查詢對於我的應用程序至關重要......您在此處提供的方法是使用重新使用查詢(如樂高件)的非常好的方式,但不幸的是,您不能在函數上使用編譯查詢,只能在表達式。 – 2009-10-09 16:18:10

+0

您是否將我上面的Func <>的性能與您的CompiledQuery版本進行了比較?您可以使用秒錶檢查執行時間。 http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx – 2009-10-09 17:05:48

+0

此外......性能無關緊要,如果它不會運行。 – 2009-10-09 17:12:00

相關問題