Linq to EF 與Linq to Object 使用心得


大家都知道Linq既可以用來查詢數據庫對象(我這里指的是Entity FrameWork里的Model對象),也可以用來查詢內存中的IEnumerable對象。

兩者單獨查詢時都不會出現什么問題,不過混合在一起時(一般是用關鍵字來join連接),要注意的地方就多着了。

 

情形1:Linq to Object 連接(join) Linq to Entity

我們首先來看這段代碼:(注意:Linq代碼里是把內存中的數據代碼,也就是Linq to object放在join前面,數據庫的數據代碼放在join后面)

[csharp]   view plain copy
  1. List<MyObject> objectList = new List<MyObject>();  
  2. objectList.Add(new MyObject { Identity = 1, Name = "Jack", Age = 30 });  
  3. objectList.Add(new MyObject { Identity = 2, Name = "Sam", Age = 28 });  
  4. objectList.Add(new MyObject { Identity = 3, Name = "Lucy", Age = 23 });  
  5.   
  6. EntityRepository repository = new EntityRepository();  
  7. DbSet<Entity> entitySet = repository.Context.Set<Entity>();  
  8.   
  9. var objectNames = (from ob in objectList  
  10.                    join en in entitySet  
  11.                    on ob.Identity equals en.SID  
  12.                    select ob.Name).ToList();  

Entity是數據庫表,有一個bigint型,名為SID字段,其他倆個為Name和Notes。上面的代碼中是把Linq to Object放在前面,Linq to Entity 放在join中,編譯運行順利。

實際的查詢數據庫的語句為:

[sql]   view plain copy
  1. SELECT   
  2. [Extent1].[SID] AS [SID],   
  3. [Extent1].[NameAS [Name],   
  4. [Extent1].[Notes] AS [Notes]  
  5. FROM [dbo].[Entity] AS [Extent1]  

這里是整表查詢。(可以打開Sql Server 2008 R2-->Performance Tools-->SQL Server Profiler, 來跟蹤此語句)。

如果把以上代碼改成select en.Name,仍然是整表查詢。

如果是設計兩個數據庫表的查詢,且最后是select 表中的某一列,則不可能是整表查詢,而是單獨查詢了某一列;而這里把entity和object放在一起,就會涉及整表查詢。此乃兩者混合使用的第一弊端。

 

情形2: Linq to Entity 連接(join) Linq to Object

數據庫的數據代碼Linq to Entity在join前,內存中數據代碼Linq to Object在join后。代碼如下:

[csharp]   view plain copy
  1. var entityNames = (from en in entitySet  
  2.                    join ob in objectList  
  3.                    on en.SID equals ob.Identity  
  4.                    select en.Name).ToList();  

好了,編譯通過,運行時拋異常了。

Only Primitive types ('Such as Int32, string, and Guid') are supported in this context

中文意思是“無法創建類型為“項目名.MyObject”的常量值。此上下文僅支持基元類型(“例如 Int32、String 和 Guid”)"

看來在涉及這種操作時,我們內存中的數據還不能是非基元類型。List<MyObject> objectList = new List<MyObject>();  

MyObject要為int32, string或者Guid,才能運行通過,並且不是整表查詢,而是針對name列的單獨查詢。大家可以一試。

 

所以在這里給出大家一點建議:

在涉及到內存中的對象與EF里的對象混合查詢時,如果內存中的對象不為基元類型,則先把其中的某個要參加匹配的變量查詢出來,再拿着它與EF對象混合查詢。

這樣不僅不會出錯,效率也高,且代碼會兩段寫,也容易看清楚意思。

以上的代碼這樣,才是最佳的

[csharp]   view plain copy
  1. IEnumerable<long> idList = objectList.Select(o => o.Identity);  
  2.   
  3. var entityNames = (from en in entitySet  
  4.                    join id in idList  
  5.                    on en.SID equals id  
  6.                    select en.Name).ToList();  

數據庫查詢語句如下:

[sql]   view plain copy
  1. SELECT   
  2. [Extent1].[NameAS [Name]  
  3. FROM  [dbo].[Entity] AS [Extent1]  
  4. INNER JOIN  (SELECT   
  5.     [UnionAll1].[C1] AS [C1]  
  6.     FROM  (SELECT   
  7.         cast(1 as bigintAS [C1]  
  8.         FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]  
  9.     UNION ALL  
  10.         SELECT   
  11.         cast(2 as bigintAS [C1]  
  12.         FROM  ( SELECT 1 AS X ) AS [SingleRowTable2]) AS [UnionAll1]  
  13. UNION ALL  
  14.     SELECT   
  15.     cast(3 as bigintAS [C1]  
  16.     FROM  ( SELECT 1 AS X ) AS [SingleRowTable3]) AS [UnionAll2] ON  CAST( [Extent1].[SID] AS bigint) = [UnionAll2].[C1]  

 

雖然里面的操作是麻煩了點,但是最后查詢出來的東西卻只有Name一個。相對而言,還是比較好的。

 

情形3:Linq to entities does not recognize the method 'Int32 get_item(int32)'

Linq to entities does not recognize the method 'Int32 get_item(int32)' method, and this method cannot be translated into a store expression

Linq to Entities 不識別方法“Int64 get_Item(Int32)”,因此該方法無法轉換為存儲表達式。

且看代碼

[csharp]   view plain copy
 
  1. List<long> list = new List<long> { 1, 2, 3 };  
  2. var entity = entitySet.Where(en => en.SID == list[1]).ToList();  

 

在這里數組的下標運算放在了linq表達式中,這種情況也是不被允許的。但是用contains,形如list.Contains(en.SID)

或者把下標操作放在linq語句都是可以的。

關系數據庫不能識別 含有下標運算的表達式樹翻譯成的sql查詢,為什么會這樣呢? 也許要去問微軟的工程師吧!

情形4:Only parameterless constructors and initializers are supported in LINQ to Entities

請看如下代碼:

[csharp]   view plain copy
 
  1. EntityRepository repository = new EntityRepository();  
  2. DbSet<Entity> entitySet = repository.Context.Set<Entity>();  
  3. List<Tuple<longstring>> tuple = entitySet.Select(en => new Tuple<longstring>(en.SID, en.Name)).ToList();  


仍然是針對EF的查詢。這里的Tuple是元組類,可用多個參數構造成一個元組類(或叫匿名類)。運行時報異常,中文意思為:

LINQ to Entities 僅支持無參數構造函數和初始值設定項。 看來是在linq中使用了帶參數的構造函數,linq是不支持這一點的。我們使用匿名類來試試看。

[csharp]   view plain copy
 
  1. var tuple = entitySet.Select(en => new { en.SID, en.Name }).ToList();  


運行正常。但是有些情況下,我們非要使用Tuple來接受這兩個參數以便於函數之間的傳值,那該怎么辦呢? 其實搞清楚真正的原因,自然就有辦法了。因為我們在select后

.ToList()時要訪問數據庫,在這種情況下,帶構造函數的Tuple自然是不能苟活的。所以只有在斷開數據庫后進行此操作才會萬無一失。

[csharp]   view plain copy
 
  1. List<Tuple<longstring>> tuple = entitySet.Select(en => new { en.SID, en.Name })  
  2.                                             .AsEnumerable()  
  3.                                             .Select(en => new Tuple<longstring>(en.SID, en.Name)).ToList();  


entitySet.Select(en => new { en.SID, en.Name })返回的是IQueryable類型,加了個AsEnumerable()之后便是IEnumerable類型,以此來斷開數據庫連接。

當然,用ToList()或ToArray()亦可!

 

轉自:http://blog.csdn.net/zzuqqiu/article/details/7185107


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM