2009-08-12 15 views
3

好,LINQ2SQL - 使用本地集合作爲一個子查詢的一部分 - 「查詢本地集合不支持」

上次我張貼了這個(上週),我沒有正確地說明問題。我已經創建了這個問題的一個快速樣本。查詢本地集合可以很好地與您一起使用,作爲基本查詢的一部分。我發現的問題是使用它與部分子查詢。例如。

這是相當難以描述沒有給你們一個數據庫圖或代碼圖,但我會盡我所能。我試圖執行我的代碼與數據庫的一個查詢。我不想分解它併發送多個命令。這樣做有一些優點,包括避免可能出現的問題,我會在最後解釋這個問題。

我加入了一些有關係的表。屬性(DataEventAttributes)表當然描述了主表中特定行(DataEvents)唯一的屬性。

當我查詢它沒有任何本地收藏時,事情工作正常,對我20 gig數據庫非常快。但是,如果我將一個本地值集合放入獲取結果的子查詢的一部分中,我將得到「不支持本地集合的查詢」

這對我來說很難在我的代碼中重現,所以我會評論它,以及我可以做你可以按照我正在做的事情。

// gets the initial query and join. We actually only care about the ID in the end, but we use the joined data 
     // to determine if a row needs to be pulled. 
     var initialQuery = from dataEvent in DataEvent.GetByQueryExpression(context) 
            join attribute in DataEventAttribute.GetByQueryExpression(context) on dataEvent.DataEventID 
             equals attribute.DataEventID 
          select new 
          { 
           ID = dataEvent.DataEventID, 
           PluginID = dataEvent.DataOwnerID, 
           TimeStamp = dataEvent.DataTimeStamp, 
           DataEventKeyID = attribute.DataEventKeyID, 
           ValueString = attribute.ValueString, 
           ValueDecimal = attribute.ValueDecimal 
          }; 

     // list of some ids that we need to confirm exist in the initial query before the final query 
     var someSetOfIDs = new List<int>() {1, 2, 3, 4, 5}; 

     // This is the local collection thats filtering out some results before I rebuild the entire result set in the final query 
     // If you comment this line out, the finalQuery will execute just fine. 
     // with this in place, the "Queries with local collections are not supported" error will come about. 
     initialQuery = initialQuery.Where(x => x.DataEventKeyID == 1 && someSetOfIDs.Contains((int) x.ValueDecimal)); 

     // reusable query for the sub queries in the results -- not part of the problem, just part of the example 
     var attributeBaseQuery = from attribute in DataEventAttribute.GetByQueryExpression(context) select attribute; 

     // Builds the final result With the IDs from the initial query 
     // the group by is to remove any duplicates that may be in the collection. 
     // the select key is getting the ID that i needed 
     // the select ID is the ID of the first item that was grouped. 
     // the contains compares the local dataEvent object with the ID table (checking to see if it exists) 
     // the result is just an example of one item I can be pulling out of the database with the new type 
     var finalQuery = from dataEvent in DataEvent.GetByQueryExpression(context) 
         where initialQuery.GroupBy(x => x).Select(x => x.Key).Select(x => x.ID).Contains(dataEvent.DataEventID) 
         select new 
            { 
             BasicData = 
             attributeBaseQuery.Where(
             attrValue => 
             attrValue.DataEventID == dataEvent.DataEventID && 
             attrValue.DataEventKeyID == (short) DataEventTypesEnum.BasicData).FirstOrDefault(). 
             ValueString 
            }; 

     var finalResult = finalQuery.Take(100).ToList(); 

的一個解決方案,我已經發現是做在finalQuery的。選擇(X => x.ID)後.ToList(),但副作用有兩個底片。一,它首先運行該查詢,並從數據庫中獲取ID ..然後它必須將這些結果作爲參數傳遞給sql server作爲參數傳遞給finalQuery。第二個主要的問題是,如果有很多來自.ToList()的結果,SQL服務器會拋出一些奇怪的錯誤信息,Google搜索顯示有很多參數被傳遞(這是有道理的,因爲參數數量可能在數千到數千)。所以說,我想弄清楚如何建立一個查詢,我可以動態調整條件,然後重建我的結果集與所有符合符合條件的子查詢的ID屬性。在SQL服務器通過工作室,這工作正常,但收集問題讓我抱着。

我已經嘗試了很多不同的方法,但似乎重現此的唯一方法是有一個使用本地集合的查詢,然後將該查詢作爲另一個查詢的一部分使用第一個查詢篩選結果。

任何想法,我可以做到這一點?

Screen shot show you know I'm not crazy.

在此先感謝您的幫助

回答

2

AFAIK,不可能在LINQ to SQL查詢中使用內存集合。我能想到的兩個可能的變通的:

選項1:每個ID執行查詢:

var someSetOfIDs = new List<int>() {1, 2, 3, 4, 5}; 

    // queryPerID will have type IEnumerable<IQueryable<'a>> 
    var queryPerID = from id in someSetOfIDs 
        select (
         from dataEvent in DataEvent.GetByQueryExpression(context) 
         join attribute in DataEventAttribute.GetByQueryExpression(context) 
         on dataEvent.DataEventID 
            equals attribute.DataEventID 
         where attribute.DataEventKeyID == 1 
           && (int)attribute.ValueDecimal == id // Changed from Contains 
         select new 
         { 
          ID = dataEvent.DataEventID, 
          PluginID = dataEvent.DataOwnerID, 
          TimeStamp = dataEvent.DataTimeStamp, 
          DataEventKeyID = attribute.DataEventKeyID, 
          ValueString = attribute.ValueString, 
          ValueDecimal = attribute.ValueDecimal 
         }); 

    // For each of those queries, we an equivalent final queryable 
    var res = from initialQuery in queryPerID 
       select (
        from dataEvent in DataEvent.GetByQueryExpression(context) 
        where initialQuery.GroupBy(x => x).Select(x => x.Key.ID).Contains(dataEvent.DataEventID) 
        select new 
        { 
         BasicData = 
          attributeBaseQuery.Where(
          attrValue => 
           attrValue.DataEventID == dataEvent.DataEventID && 
           attrValue.DataEventKeyID == (short) DataEventTypesEnum.BasicData).FirstOrDefault(). 
           ValueString 
        }) into finalQuery 
       from x in finalQuery 
       select x; 

    var finalResult = finalQuery.Take(100).ToList(); 

我不知道,如果連編譯,但它應該是八九不離十。

選項2:從someSetOfIDs構建謂詞表達式以傳遞給SQL。

 var someSetOfIDs = new List<decimal>() { 1, 2, 3, 4, 5 }; 

     Expression<Func<DataEventAttribute, bool>> seed = x => false; 
     var predicate = someSetOfIDs.Aggregate(seed, 
      (e, i) => Expression.Lambda<Func<DataEventAttribute, bool>>(
       Expression.OrElse(
        Expression.Equal(
         Expression.Property(
          e.Parameters[0], 
          "ValueDecimal"), 
         Expression.Constant(i)), 
        e.Body), 
       e.Parameters)); 

本質上講,我們已經建立了一個WHERE子句:

x => ((x.ValueDecimal = 5) || ((x.ValueDecimal = 4) || ((x.ValueDecimal = 3) || 
((x.ValueDecimal = 2) || ((x.ValueDecimal = 1) || False))))) 

重要的是要注意,這種方法不會與匿名類型,所以你將不得不使用謂詞上可查詢是非常重要的一個命名的類型。這不是一個問題,如果你重新組織一個位(和可能會產生更好的查詢計劃,實際上):

var attributes = DataEventAttribute.GetByQueryExpression(context) 
        .Where(a => a.DataEventKeyID ==1) 
        .Where(predicate); 

    var initialQuery = from dataEvent in DataEvent.GetByQueryExpression(context) 
         join attribute in attributes 
         select new 
         { 
          ID = dataEvent.DataEventID, 
          PluginID = dataEvent.DataOwnerID, 
          TimeStamp = dataEvent.DataTimeStamp, 
          DataEventKeyID = attribute.DataEventKeyID, 
          ValueString = attribute.ValueString, 
          ValueDecimal = attribute.ValueDecimal 
         }; 
+0

Dahlbyk, 非常感謝。選項2是要走的路。 ([t5]。[ValueDecimal] = @ p1)OR([t5]。[ValueDecimal] = @ p2)OR([t5]。[ValueDecimal] = @ p3)OR([ t5]。[ValueDecimal] = @ p4)OR([t5]。[ValueDecimal] = @ p5))AND([t5]。[DataEventKeyID] = @ p6) 這正是我想要/需要的。 你在例子中顯示的內容實際上延伸了我的思維過程,並且可能已經解決了我腦海中的其他想法。 非常感謝您的解決方案! – TravisWhidden 2009-08-13 15:06:51

0

我沒有這方面的專家,但LinqToSql的工作原理是建立一個表達式樹是在執行點轉換成SQL查詢。如果您的所有查詢都可以轉換爲SQL,此工作正常。但是,你正在做的是基本上試圖將你的SQL查詢與.NET對象集合結合起來。麻煩的是,這不起作用,因爲連接不能被轉換成SQL查詢。您正在混合兩種不同的東西 - LinqToSql和LinqToObjects。在您的LinqToSql上調用ToList()使其能夠像您一樣回到LinqToObjects的域中。對不起,恐怕我不知道這個。

PS。也許看到這個問題:Linq2Sql -> Searching the database against a local collection of values - Queries with local collections are not supported

+0

的LINQ to SQL確實允許對本地收藏測試,但並非在每種情況。 – 2009-08-12 20:00:10

+0

謝謝丹。如果你只是將子IQueryable從查詢中提取出來,並且自己運行它,這實際上可以工作。您可以根據Linq2Sql查詢列表本地列表/集合,並將本地對象/集合轉換爲nice -in-statement。我本人試圖找出如何使用select來填充「In」(又名包含在linq2sql中)的內容,而不具有值。在哪裏(從某個表中選擇someID來說明哪裏)。達爾比克對謂詞有一個好主意。但我會記住這一點。感謝您的反饋! – TravisWhidden 2009-08-13 15:02:44

+0

非常好,那是個好消息。我猜想一個簡單的值類型列表,比如Ints可以用作SQL IN語句。我正在考慮更通用的對象集合,這顯然不容易兌換。無論如何,學習一些東西很好,所以謝謝。 – 2009-08-13 20:05:17