你們都知道Linq既能夠用來查詢數據庫對象(我這裏指的是Entity FrameWork裏的Model對象),也能夠用來查詢內存中的IEnumerable對象。sql
二者單獨查詢時都不會出現什麼問題,不過混合在一塊兒時(通常是用關鍵字來join鏈接),要注意的地方就多着了。數據庫
情形1:Linq to Object 鏈接(join) Linq to Entity
咱們首先來看這段代碼:(注意:Linq代碼裏是把內存中的數據代碼,也就是Linq to object放在join前面,數據庫的數據代碼放在join後面)express
- List<MyObject> objectList = new List<MyObject>();
- objectList.Add(new MyObject { Identity = 1, Name = "Jack", Age = 30 });
- objectList.Add(new MyObject { Identity = 2, Name = "Sam", Age = 28 });
- objectList.Add(new MyObject { Identity = 3, Name = "Lucy", Age = 23 });
-
- EntityRepository repository = new EntityRepository();
- DbSet<Entity> entitySet = repository.Context.Set<Entity>();
-
- var objectNames = (from ob in objectList
- join en in entitySet
- on ob.Identity equals en.SID
- select ob.Name).ToList();
Entity是數據庫表,有一個bigint型,名爲SID字段,其餘倆個爲Name和Notes。上面的代碼中是把Linq to Object放在前面,Linq to Entity 放在join中,編譯運行順利。數組
實際的查詢數據庫的語句爲:less
- SELECT
- [Extent1].[SID] AS [SID],
- [Extent1].[Name] AS [Name],
- [Extent1].[Notes] AS [Notes]
- FROM [dbo].[Entity] AS [Extent1]
這裏是整表查詢。(能夠打開Sql Server 2008 R2-->Performance Tools-->SQL Server Profiler, 來跟蹤此語句)。函數
若是把以上代碼改爲select en.Name,仍然是整表查詢。ui
若是是設計兩個數據庫表的查詢,且最後是select 表中的某一列,則不多是整表查詢,而是單獨查詢了某一列;而這裏把entity和object放在一塊兒,就會涉及整表查詢。此乃二者混合使用的第一弊端。this
情形2: Linq to Entity 鏈接(join) Linq to Object
數據庫的數據代碼Linq to Entity在join前,內存中數據代碼Linq to Object在join後。代碼以下:spa
- var entityNames = (from en in entitySet
- join ob in objectList
- on en.SID equals ob.Identity
- select en.Name).ToList();
好了,編譯經過,運行時拋異常了。.net
Only Primitive types ('Such as Int32, string, and Guid') are supported in this context
中文意思是「沒法建立類型爲「項目名.MyObject」的常量值。此上下文僅支持基元類型(「例如 Int3二、String 和 Guid」)"
看來在涉及這種操做時,咱們內存中的數據還不能是非基元類型。List<MyObject> objectList = new List<MyObject>();
MyObject要爲int32, string或者Guid,才能運行經過,而且不是整表查詢,而是針對name列的單獨查詢。你們能夠一試。
因此在這裏給出你們一點建議:
在涉及到內存中的對象與EF裏的對象混合查詢時,若是內存中的對象不爲基元類型,則先把其中的某個要參加匹配的變量查詢出來,再拿着它與EF對象混合查詢。
這樣不只不會出錯,效率也高,且代碼會兩段寫,也容易看清楚意思。
以上的代碼這樣,纔是最佳的
- IEnumerable<long> idList = objectList.Select(o => o.Identity);
-
- var entityNames = (from en in entitySet
- join id in idList
- on en.SID equals id
- select en.Name).ToList();
數據庫查詢語句以下:
- SELECT
- [Extent1].[Name] AS [Name]
- FROM [dbo].[Entity] AS [Extent1]
- INNER JOIN (SELECT
- [UnionAll1].[C1] AS [C1]
- FROM (SELECT
- cast(1 as bigint) AS [C1]
- FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
- UNION ALL
- SELECT
- cast(2 as bigint) AS [C1]
- FROM ( SELECT 1 AS X ) AS [SingleRowTable2]) AS [UnionAll1]
- UNION ALL
- SELECT
- cast(3 as bigint) AS [C1]
- 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)」,所以該方法沒法轉換爲存儲表達式。
且看代碼
- List<long> list = new List<long> { 1, 2, 3 };
- 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
請看以下代碼:
- EntityRepository repository = new EntityRepository();
- DbSet<Entity> entitySet = repository.Context.Set<Entity>();
- List<Tuple<long, string>> tuple = entitySet.Select(en => new Tuple<long, string>(en.SID, en.Name)).ToList();
仍然是針對EF的查詢。這裏的Tuple是元組類,可用多個參數構形成一個元組類(或叫匿名類)。運行時報異常,中文意思爲:
LINQ to Entities 僅支持無參數構造函數和初始值設定項。 看來是在linq中使用了帶參數的構造函數,linq是不支持這一點的。咱們使用匿名類來試試看。
- var tuple = entitySet.Select(en => new { en.SID, en.Name }).ToList();
運行正常。可是有些狀況下,咱們非要使用Tuple來接受這兩個參數以便於函數之間的傳值,那該怎麼辦呢? 其實搞清楚真正的緣由,天然就有辦法了。由於咱們在select後
.ToList()時要訪問數據庫,在這種狀況下,帶構造函數的Tuple天然是不能苟活的。因此只有在斷開數據庫後進行此操做纔會萬無一失。
- List<Tuple<long, string>> tuple = entitySet.Select(en => new { en.SID, en.Name })
- .AsEnumerable()
- .Select(en => new Tuple<long, string>(en.SID, en.Name)).ToList();
entitySet.Select(en => new { en.SID, en.Name })返回的是IQueryable類型,加了個AsEnumerable()以後即是IEnumerable類型,以此來斷開數據庫鏈接。
固然,用ToList()或ToArray()亦可!