2015-04-17 78 views
10

我有下面的代碼示例查詢產品列表。
Linq to Entities Group By(OUTER APPLY)「oracle 11.2.0.3.0不支持應用」

var productResults = Products.Where((p) => refFilterSequence.Contains(p.Ref)) 
       .GroupBy(g => g.Code, (key, g) => g.OrderBy(whp => whp.Ref).First()).ToList(); 


這工作完全按照預期和返回4行我在內存中收集使用時想要的,但對Oracle數據庫運行時:

.GroupBy(g => g.Code, (key, g) => g.OrderBy(whp => whp.Ref).First()) 

這將引發一個錯誤說我應該使用FirstOrDefault,這在Oracle數據庫中不受支持。錯誤oracle 11.2.0.3.0不支持應用被拋出。 Googleing在CodePlex上揭示了這一點:https://entityframework.codeplex.com/workitem/910

使用以下二進制文件時,會出現此:

  • 的EntityFramework 6.0.0.0
  • Oracle.ManagedDataAccess 4.121.2.0
  • Oracle.ManagedDataAccess.EntityFramework 6.121.2.0
  • .Net框架4.5。 1

該數據庫是一個Oracle 11.2.0.3.0數據庫。

生成的SQL使用了Oracle的11.2.0.3.0版本不支持的OUTER APPLY(見下圖),那麼爲什麼EF/Oracle.ManagedDataAccess試圖使用它呢?有沒有辦法告訴EF不要使用APPLY關鍵字?

SQL

頁面下方說,適用於甲骨文12C 1版加入支持,但我不能更新我的所有的數據庫只是爲了讓一個GROUP BY工作。 http://www.oracle.com/technetwork/database/windows/newfeatures-084113.html

看來,這是一個已知的問題(Known Issues in SqlClient for Entity Framework):

以下是可能導致 存在CROSS的一些典型場景APPLY和/或OUTER APPLY運營商在輸出 查詢:

  • 使用接受元素選擇器的分組方法的LINQ查詢。

之前,我求助於創建視圖(我會創建多個數據庫視圖),任何人都可以看到另一種解決方案?

任何有興趣,會做我想做的針對該數據庫版本的SQL看起來像下面這樣:

select * 
from (select RANK() OVER (PARTITION BY sm.product ORDER BY refs.map)  ranking, sm.* 
      from schema.table sm, 
        (
         select 'R9' ref, 0 map from dual 
         union all 
         select 'R1' ref, 1 map from dual 
         union all 
         select 'R6' ref, 2 map from dual 
        ) refs 
      where sm.ref= refs.ref        
     ) stock 
where ranking = 1 

的代碼最終將傳遞到Web和OData的控制器服務類API。 下面的例子使用演示數據,真實的數據庫有700,000條記錄, ,所以我想避免執行查詢,讓OData處理頁面限制和進一步過濾。

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace DemoApp 
{ 
    class Program 
    { 
     public class Product 
     { 
      public string Ref { get; set; } 
      public string Code { get; set; } 
      public int Quantity { get; set; } 
     } 

     //demo data 
     static readonly List<Product> Products = new List<Product> 
     { 
      new Product { Ref = "B2", Code = "ITEM1", Quantity = 1}, 
      new Product { Ref = "R1", Code = "ITEM1", Quantity = 2}, 
      new Product { Ref = "R9", Code = "ITEM1", Quantity = 3}, 
      new Product { Ref = "R9", Code = "ITEM2", Quantity = 4}, 
      new Product { Ref = "R6", Code = "ITEM2", Quantity = 5}, 
      new Product { Ref = "B2", Code = "ITEM3", Quantity = 6}, 
      new Product { Ref = "R1", Code = "ITEM3", Quantity = 7}, 
      new Product { Ref = "R9", Code = "ITEM3", Quantity = 8}, 
      new Product { Ref = "B2", Code = "ITEM4", Quantity = 9}, 
      new Product { Ref = "X3", Code = "ITEM4", Quantity = 10}, 
      new Product { Ref = "B8", Code = "ITEM5", Quantity = 10}, 
      new Product { Ref = "R6", Code = "ITEM5", Quantity = 12}, 
      new Product { Ref = "M2", Code = "ITEM5", Quantity = 13}, 
      new Product { Ref = "R1", Code = "ITEM5", Quantity = 14}, 
     }; 

    static void Main(string[] args) 
    { 
     // this array is of variable length, and will not always contain 3 items. 
     var refFilterSequence = new List<string> {"R9", "R1", "R6"}; 

     var results = GetProductsForODataProcessing(refFilterSequence); 

     // some further filtering may occur after the queryable is returned. 
     // the actual implmentation is an OData Web API, so filters, expansions etc could be added. 

     //results = results.Where(p => p.Quantity > 2); 

     results.ToList().ForEach(p => Console.WriteLine("RANK:{0}\tREF:{1}\tCode:{2}\tQty:{3}", "?", p.Ref, p.Code, p.Quantity)); 
     Console.ReadLine(); 
    } 

    static IQueryable<Product> GetProductsForODataProcessing(List<string> filterSequence) 
    { 
     var productResults = Products.Where((p) => filterSequence.Contains(p.Ref)) 
      .GroupBy(g => g.Code, (key, g) => g.OrderBy(whp => whp.Ref).First()).AsQueryable(); 

     return productResults;    
    } 
} 


// Example Output 
// ....................... 
// REF:R1 Code:ITEM1 Qty:2 
// REF:R6 Code:ITEM2 Qty:3 
// REF:R1 Code:ITEM3 Qty:7 
// REF:R1 Code:ITEM5 Qty:14 

回答

1

由於您可以自己編寫查詢。也許你可以用它創建一個存儲過程,並從實體框架調用SP。

+0

這與創建我在問題中提到的視圖相同。雖然這會起作用,但我必須在全球的幾個數據庫上部署和維護存儲過程/視圖。 – philreed

+0

您可以嘗試使用DbSet.SqlQuery方法,然後:https://msdn.microsoft.com/en-us/data/jj592907.aspx – PabloR

+0

這不是一個壞主意,我錯誤地認爲SqlQuery不會返回IQueryable。當我下週回到辦公室時,我會放棄這一點。謝謝。 – philreed

0

您可以嘗試在GroupBy之前執行ToArray,因此它在內存中執行。這不會是最佳的表現,但應該工作。

var productResults = Products.Where((p) => refFilterSequence.Contains(p.Ref)).ToArray() 
        .GroupBy(g => g.Code, (key, g) => g.OrderBy(whp => whp.Ref).First()).ToList(); 
+1

這樣做的問題是在這種情況下會有700,000條記錄被加載到內存中。 – philreed

-2

Oracle 11不支持APPLY。然而,Oracle 12的確如此。

+0

我明白這一點,並在我原來的問題中提到它。我想真正的問題是,爲什麼Entity框架會生成目標數據庫不支持的SQL? – philreed

+0

EF的第一個目標是SQL Server,它支持APPLY。在LINQ查詢提供程序中支持多個數據庫非常困難。 EF的查詢提供者可能是從LINQ to SQL派生而來的。有一個有趣的開源LINQ查詢提供程序,可能由同一個人編寫,支持多個數據庫;它被稱爲IQToolkit。您可以在那裏看到APPLY是如何綁定到查詢分析中的,這種方式在SQL生成時並不容易阻止。 EF在版本7中進行了重寫,Oracle 12支持APPLY;希望其中的一個提供修復。 –

相關問題