在本篇中,我们将接着上一篇“LINQ to SQL 和 Entity Framework(上)”的内容,继续使用LINQ to SQL和Entity Framework来实践“解释查询”,学习这些技术的关键特性。我们在此关注的是LINQ to SQL和Entity Framework中的”LINQ”部分,并会比较这两种技术的相同和不同之处。通过我们之前介绍的LINQ知识还有将来会讨论的更多LINQ Operators,相信阅者能针对LINQ to SQL和Entity Framework写出优雅高效的查询。为了简单清晰,文中有些地方对LINQ to SQL和Entity Framework进行了缩写,分别为:L2S和EF。

LINQ to SQL和Entity Framework的延迟执行

和本地查询一样,L2S和EF查询也是延迟执行的,这样就允许我们渐进地创建LINQ查询。但是,有一个方面,L2S和EF有自己特殊的延迟执行语义,这就是当一个子查询出现在Select表达式中时:

  • 对于本地查询,你获得了两个延迟执行,因为从功能角度来看,你选择了包含多个查询的一个sequence。若以当你遍历外层结果sequence时,并不会遍历内部子查询,所以子查询此时也就不会执行。
  • 而对于L2S/EF,子查询和外层的主查询在同一时间被执行,这样就避免了过度的连接远程数据库导致性能问题。

比如,对于L2S/EF,下面的查询在第一个foreach语句时执行,且只执行一次:

            var context = new LifePoemContext("database connection string");

            var query = from c in context.Customers
select
from o in context.Orders
select new { c.Name, o.Price }; foreach (var customerOrders in query)
foreach (var namePrice in customerOrders)
Console.WriteLine(namePrice.Name + " spent " + namePrice.Price);

换句话说,我们在select表达式中明确指定的EntitySets/EntityCollections总是在一次执行中就能被获取到:

            var context = new LifePoemContext("database connection string");
var query = from c in context.Customers
select new { c.Name, c.Orders }; foreach (var row in query)
foreach (var order in row.Orders) // 没有额外的连接查询
Console.WriteLine(row.Name + " spent " + order.Price);

但如果我们没有事先进行数据转换,就对EntitySet/EntityCollection属性进行遍历的话,就会适用于延迟执行。下面的示例中,L2S和EF在每一次循环中都会执行另外的Orders查询:

            context.ContextOptions.DeferredLoadingEnabled = true;   // 仅EF需要此句

            foreach (Customer c in context.Customers)
foreach (Order o in c.Orders) // 每次都会开始一个新的SQL查询
Console.WriteLine(c.Name + " spent " + o.Price);

这种模式在我们需要有条件的执行内部查询时具有优势,比如我们可能需要依靠客户端来做某个条件测试时:

            foreach (Customer c in context.Customers)
if(myWebService.HasBadCreditHistory(c.ID))
foreach (Order o in c.Orders) // 开始一个新的SQL查询
Console.WriteLine(c.Name + " spent " + o.Price);

上面我们看到了如何对关联属性进行显示的数据转换(select)来避免重复执行。稍后我们就会看到,L2S和EF还提供了其他的机制来实现这个功能。

DataLoadOptions

DataLoadOptions类是L2S的特性,它有两个特殊的作用:

  • AssociateWith让你能够事先对EntitySet关联设置过滤条件
  • LoadWith让你能够设置某些EntitySets为主动加载(eager loading),从而减少连接数据库的次数

设置过滤条件

假如我们只关心那些Price大于1000的Orders,我们就可以通过DataLoadOptions来设置过滤条件:

            var context = new LifePoemContext("database connection string");

            DataLoadOptions options = new DataLoadOptions();
options.AssociateWith<Customer>(c => c.Orders.Where(order => order.Price > ));
context.LoadOptions = options; foreach (Customer c in context.Customers)
if (myWebService.HasBadCreditHistory(c.ID))
ProcessCustomer(c); // 如果在该方法中引用c.Orders,只有那些Price > 1000的Orders被返回

这会指示我们的DataContext实例总是使用给定的条件对Customer的Orders进行过滤。需要注意的是,AssociateWith并不会改变延迟执行的语义,它只是命令对特定的关系进行隐式的过滤。

主动加载(Eager Loading)

DataLoadOptions的第二个作用是请求让某个EntitySets跟随父EntitySets一起加载。比如,假设你想在加载所有Customers的同时一起加载他们的Orders,而不是对每一个Customer分别查询一次Orders:

            var context = new LifePoemContext("database connection string");

            DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Customer>(c => c.Orders);
context.LoadOptions = options; foreach (Customer c in context.Customers) // 一次查询
foreach (Order o in c.Orders) // 因为上面的DataLoadOptions,所有的Orders都在上面的查询中被同时Load了
Console.WriteLine(c.Name + " bought a " + o.Description);

这会指示DataContext,不论何时,只要一个Customer被获取,它的Orders也会在同一时间被加载。我们可以组合LoadWith和AssociateWith方法,这样既能获得主动加载,还能对加载的EntitySets进行过滤,比如:

            DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Customer>(c => c.Orders);
options.AssociateWith<Customer>(c => c.Orders.Where(order => order.Price > ));

Entity Framework中的主动加载(Eager Loading)

在Entity Framework中,如果我们希望某个关联的EntitySets被主动加载,则可以使用Include方法。下面的代码就是在一个SQL查询中,获取所有的Customers和他们的Orders:

            foreach (var c in context.Customers.Include("Orders"))
foreach (var order in c.Orders)
Console.WriteLine(order.Description);

Include可以被级联使用,假如每个Order都有OrderDetails和SalesPersons导航属性的话,我们可以写出如下的查询让这些数据也被一同加载:

            context.Customers.Include("Orders.OrderDetails")
.Include("Orders.SalesPersons")

更新

L2S和EF也会跟踪你对Entities所做的修改并允许你把他们更新到数据库。对于L2S,我们调用DataContext对象的SubmitChanges方法;而对于EF,我们调用ObjectContext对象的SaveChagnes方法。

L2S的Table<>类提供了InsertOnSubmit和DeleteOnSubmit方法让我们从一个表中插入或删除行;EF的ObjectSet<>类则提供了AddObject和DeleteObject方法来实现相同的功能,请看下面的代码示例:

            var context = new LifePoemContext("database connection string");

            Customer cust = new Customer { ID = , Name = "Yoyoo" };
context.Customers.InsertOnSubmit(cust); // 插入Customer,在EF中使用AddObject
context.SubmitChanges(); // 在EF中使用SaveChanges // 现在我们获取上面插入的数据行,对其进行更新,然后删除它
Customer cust2 = context.Customers.Single(c => c.ID == );
cust2.Name = "Yoyoo2";
context.SubmitChanges(); // 更新Customer context.Customers.DeleteOnSubmit(cust2); //在EF中使用DeleteObject
context.SubmitChanges(); // 删除Customer

SubmitChanges/SaveChanges会收集自context创建(或上一次Save)以来对entities所做的所有修改,然后执行一个SQL语句来把他们写回数据库。

我们还可以调用Add方法来向一个EntitySet/EntitiyCollection添加数据行,在执行SubmitChanges或SaveChanges,L2S和EF会自动生成相应的外键:

            Order o1 = new Order { ID = , OrderDate = DateTime.Now, Price =  };
Order o2 = new Order { ID = , OrderDate = DateTime.Now, Price = }; Customer cust = context.Customers.Single(c => c.ID == );
cust.Orders.Add(o1);
cust.Orders.Add(o2); context.SubmitChanges();

在这个例子中,L2S/EF会自动把外键值1写入新增的Order的CustomerID列,这是因为我们为Customer和Order定义了关联属性,如:

        // With L2S
[Association(Name="Customer_Order", Storage="_Orders", ThisKey="ID", OtherKey="CustomerID")]
public EntitySet<Order> Orders { get {...} set {...} }

当你从一个EntitySet/EntityCollection中移除某行数据时,他的外键列会被自动设置为null。下面的代面会在我们最近新增的两个orders和他们的Customer之间移除关联,注意,只是去除关联,Remove并不会删除子entities:

            var context = new LifePoemContext("database connection string");

            Customer cust = context.Customers.Single(c => c.ID == );
cust.Orders.Remove(cust.Orders.Single(order => order.ID == ));
cust.Orders.Remove(cust.Orders.Single(order => order.ID == )); context.SubmitChanges();

因为上面的代码会把每个Order的CustomerID列设为空,所以数据库中Order.CustomerID列必需是可空的,否则会抛出异常。

如果我们要完全删除子entities,则需要调用DeleteOnSubmit:

            // with L2S
context.Orders.DeleteOnSubmit(context.Orders.Single(order => order.ID == ));
context.Orders.DeleteOnSubmit(context.Orders.Single(order => order.ID == ));
context.SubmitChanges(); // with EF
context.Orders.DeleteObject(context.Orders.Single(order => order.ID == ));
context.Orders.DeleteObject(context.Orders.Single(order => order.ID == ));
context.SaveChanges();

LINQ to SQL和Entity Framework的API对比

正如我们在这两篇文章中看到的那样,L2S和EF在LINQ查询和数据更新方面非常相似,只是创建的对象或调用的方法有所不同罢了,下表总结了他们的API差异:

目的

LINQ to SQL

Entity Framework

获取保持所有CRUD操作的类

DataContext

ObjectContext

从数据库中(延迟)获取某种类型的所有entities

GetTable

CreateObjectSet

上面方法的返回类型

Table<T>

ObjectSet<T>

提交对实体对象的更新

SubmitChanges

SaveChanges

新增一个entity

InsertOnSubmit

AddObject

删除一个entity

DeleteOnSubmit

DeleteObject

代表关联属性(有多个相关entities的那一方)的类型

EntitySet<T>

EntityCollection<T>

代表关联属性(有多个相关entities的那一方)的类型(字段类型)

EntityRef<T>

EntityReference<T>

装载关联属性时的默认策略

自动延迟加载

明确调用

主动加载(eager loading)

DataLoadOptions

.Include()

通过这两篇文章,我们有针对性的了解了L2S和EF在LINQ查询支持上的特性。通过比较他们的异同,让我们更好的他们的内在联系和区分他们在使用上的差别。在接下来的几篇博客中,我准备对LINQ查询运算符(LINQ Operators)进行更加详细的分类介绍。只有在了解了大多数查询运算符后,才能更好的写出功能强大而又简洁优雅的 LINQ查询。

LINQ之路10:LINQ to SQL 和 Entity Framework(下)的更多相关文章

  1. LINQ之路 9:LINQ to SQL 和 Entity Framework(上)

    在上一篇中,我们从理论和概念上详细的了解了LINQ的第二种架构“解释查询”.在这接下来的二个篇章中,我们将使用LINQ to SQL和Entity Framework来实践“解释查询”,学习这些技术的 ...

  2. LINQ to SQL和Entity Framework对比与关联 (转载)

    LINQ to SQL和Entity Framework对比与关联       LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在 ...

  3. LINQ to SQL和Entity Framework对照

    LINQ to SQL和Entity Framework都是一种包括LINQ功能的对象关系映射技术.他们之间的本质差别在于EF对数据库架构和我们查询的类型实行了更好的解耦. 使用EF,我们查询的对象不 ...

  4. LINQ TO SQL和Entity Framework 的关系 你了解多少?

    1. LINQ  TO SQL  和EF 特点:  LINQ TO SQL和Entity Framework都是一种包含LINQ功能的ORM 也就是所谓的关系对象的映射.其中包括的有DBFrist   ...

  5. 在Linq to sql 和 Entity framework 中使用lambda表达式实现left join

    在Linq to sql 和 Entity framework 中使用lambda表达式实现left join 我们知道lambda表达式在Linq to sql 和 Entity framework ...

  6. LINQ to SQL和Entity Framework

    LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术. 那么为什么会有LINQ这个东西的出现呢. 简单来说LINQ是为了满足不知道怎么操作数据库的程序员开 ...

  7. Linq实战 之 Linq to Sql及Entity Framework操作详解

    Linq实战 之 Linq to Sql及Entity Framework操作详解 一:linq to db的框架 1. linq to sql 2. linq to ado.net entity f ...

  8. LINQ to SQL和Entity Framework对比与关联

    LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在于EF对数据库架构和我们查询的类型实行了更好的解耦.使用EF,我们查询的对象不再 ...

  9. 查询大数据表的效率对比:Linq to SQL、Entity Framework、企业库存储过程、ADO.Net

    最近因为要开发大数据量网站,特作比较. Linq to SQL 查询 记录数:399997Linq to SQL 查询 Milliseconds:1910视图查询 记录数:399997视图查询 Mil ...

随机推荐

  1. linux查看进程启动时间

    1. ps axu 2. 精确查看 for pid in $(pgrep httpd); do echo -n "${pid} " ; ps -p ${pid} -o lstart ...

  2. AutoBundle in asp.net mvc 5

    using System.Collections.Concurrent; using System.Text; namespace System.Web.Optimization { public s ...

  3. (转)HTTP 长连接和短连接

    1. HTTP协议与TCP/IP协议的关系 HTTP的长连接和短连接本质上是TCP长连接和短连接.HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议.IP协议主要解决网络路由和寻址问 ...

  4. android adb命令

    adb查看logcat adb logcat <TAG>:* *:S 查看指定TAG的Log adb查看最上层activity:linux: adb shell dumpsys activ ...

  5. 解决Android与服务器交互大容量数据问题

    对于目前的状况来说,移动终端的网络状况没有PC网络状况那么理想.在一个Android应用中,如果需要接收来自服务器的大容量数据,那么就不得不考虑客户的流量问题.本文根据笔者的一个项目实战经验出发,解决 ...

  6. solr 日期查询格式

    //solr 日期格式: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //开始 ...

  7. Mac下搭建git

    一.在本地git库中添加用户名及邮箱 git config --global user.name "username" git config --global user.email ...

  8. html5 webDatabase 存储中sql语句执行可嵌套使用

    html5 webDatabase 存储中sql语句执行可嵌套使用,代码如下: *); data.transaction(function(tx){ tx.executeSql("creat ...

  9. UIFont的常用字体

    + (UIFont *)systemFontOfSize:(CGFloat)fontSize;   系统默认字体 + (UIFont *)boldSystemFontOfSize:(CGFloat)f ...

  10. DataTable与实体类互相转换

    /// <summary> /// DataTable与实体类互相转换 /// </summary> /// <typeparam name="T"& ...