【转】Linq to EF 与Linq to Object 使用心得
2015-01-15 13:58
369 查看
大家都知道Linq既可以用来查询数据库对象(我这里指的是Entity FrameWork里的Model对象),也可以用来查询内存中的IEnumerable对象。
两者单独查询时都不会出现什么问题,不过混合在一起时(一般是用关键字来join连接),要注意的地方就多着了。
[csharp] view plaincopy
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中,编译运行顺利。
实际的查询数据库的语句为:
[sql] view plaincopy
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,仍然是整表查询。
如果是设计两个数据库表的查询,且最后是select 表中的某一列,则不可能是整表查询,而是单独查询了某一列;而这里把entity和object放在一起,就会涉及整表查询。此乃两者混合使用的第一弊端。
[csharp] view plaincopy
var entityNames = (from en in entitySet
join ob in objectList
on en.SID equals ob.Identity
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 plaincopy
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();
数据库查询语句如下:
[sql] view plaincopy
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一个。相对而言,还是比较好的。
Linq to Entities 不识别方法“Int64 get_Item(Int32)”,因此该方法无法转换为存储表达式。
且看代码
[csharp] view plaincopy
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查询,为什么会这样呢? 也许要去问微软的工程师吧!
[csharp] view plaincopy
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是不支持这一点的。我们使用匿名类来试试看。
[csharp] view plaincopy
var tuple = entitySet.Select(en => new { en.SID, en.Name }).ToList();
运行正常。但是有些情况下,我们非要使用Tuple来接受这两个参数以便于函数之间的传值,那该怎么办呢? 其实搞清楚真正的原因,自然就有办法了。因为我们在select后
.ToList()时要访问数据库,在这种情况下,带构造函数的Tuple自然是不能苟活的。所以只有在断开数据库后进行此操作才会万无一失。
[csharp] view plaincopy
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()亦可!
两者单独查询时都不会出现什么问题,不过混合在一起时(一般是用关键字来join连接),要注意的地方就多着了。
情形1:Linq to Object 连接(join) Linq to Entity
我们首先来看这段代码:(注意:Linq代码里是把内存中的数据代码,也就是Linq to object放在join前面,数据库的数据代码放在join后面)[csharp] view plaincopy
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中,编译运行顺利。
实际的查询数据库的语句为:
[sql] view plaincopy
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,仍然是整表查询。
如果是设计两个数据库表的查询,且最后是select 表中的某一列,则不可能是整表查询,而是单独查询了某一列;而这里把entity和object放在一起,就会涉及整表查询。此乃两者混合使用的第一弊端。
情形2: Linq to Entity 连接(join) Linq to Object
数据库的数据代码Linq to Entity在join前,内存中数据代码Linq to Object在join后。代码如下:[csharp] view plaincopy
var entityNames = (from en in entitySet
join ob in objectList
on en.SID equals ob.Identity
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 plaincopy
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();
数据库查询语句如下:
[sql] view plaincopy
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 expressionLinq to Entities 不识别方法“Int64 get_Item(Int32)”,因此该方法无法转换为存储表达式。
且看代码
[csharp] view plaincopy
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
请看如下代码:[csharp] view plaincopy
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是不支持这一点的。我们使用匿名类来试试看。
[csharp] view plaincopy
var tuple = entitySet.Select(en => new { en.SID, en.Name }).ToList();
运行正常。但是有些情况下,我们非要使用Tuple来接受这两个参数以便于函数之间的传值,那该怎么办呢? 其实搞清楚真正的原因,自然就有办法了。因为我们在select后
.ToList()时要访问数据库,在这种情况下,带构造函数的Tuple自然是不能苟活的。所以只有在断开数据库后进行此操作才会万无一失。
[csharp] view plaincopy
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()亦可!
相关文章推荐
- Linq to EF 与Linq to Object 使用心得
- Linq to EF 与Linq to Object 使用心得
- Linq to EF 与Linq to Object 使用心得
- Linq to EF 与Linq to Object 使用心得
- Linq to EF 与Linq to Object 使用心得
- 如何构建和使用EF,用linqToSql实现对数据的操作
- EF Linq To Entities ObjectManager 已存在相同键无法再Attach的解决方案
- LINQ&EF任我行(二)--LinQ to Object (转)
- LINQ&EF任我行(二)--LinQ to Object
- Linq To EF 使用小知识(添加记录后获取添加的自增ID和叫“ID”的列不是自增列不让插入的问题)
- LINQ&EF任我行(二)--LinQ to Object (转)
- LINQ&EF任我行(二)--LinQ to Object
- LINQ&EF任我行(二)--LinQ to Object
- LINQ&EF任我行(二)--LinQ to Object
- Linq To EF 使用小知识(添加记录后获取添加的自增ID和叫“ID”的列不是自增列不让插入的问题)
- 使用LINQ TO SQL 的CodeSmith模板(1)
- 在LINQ to SQL中使用Translate方法以及修改查询用SQL 推荐
- 地磅称量系统之(17~23)使用LINQ to SQL Classes模板将数据表和存储过程映射成对象
- Silverlight 2 (beta1)数据操作(5)——使用LINQ to SQL进行数据CRUD操作(上)