LINQ之路10:LINQ to SQL 和 Entity Framework(下)

在本篇中,我们将接着上一篇“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 > 1000));            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 > 1000));

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 = 1000, Name = "Yoyoo" };            context.Customers.InsertOnSubmit(cust);     // 插入Customer,在EF中使用AddObject            context.SubmitChanges();                    // 在EF中使用SaveChanges

            // 现在我们获取上面插入的数据行,对其进行更新,然后删除它            Customer cust2 = context.Customers.Single(c => c.ID == 1000);            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 = 100, OrderDate = DateTime.Now, Price = 100 };            Order o2 = new Order { ID = 101, OrderDate = DateTime.Now, Price = 500 };

            Customer cust = context.Customers.Single(c => c.ID == 1);            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 == 1);            cust.Orders.Remove(cust.Orders.Single(order => order.ID == 100));            cust.Orders.Remove(cust.Orders.Single(order => order.ID == 101));

            context.SubmitChanges();

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

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

            // with L2S            context.Orders.DeleteOnSubmit(context.Orders.Single(order => order.ID == 100));            context.Orders.DeleteOnSubmit(context.Orders.Single(order => order.ID == 101));            context.SubmitChanges();

            // with EF            context.Orders.DeleteObject(context.Orders.Single(order => order.ID == 100));            context.Orders.DeleteObject(context.Orders.Single(order => order.ID == 101));            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查询。

时间: 2024-10-13 16:03:21

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

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

在上一篇中,我们从理论和概念上详细的了解了LINQ的第二种架构“解释查询”.在这接下来的二个篇章中,我们将使用LINQ to SQL和Entity Framework来实践“解释查询”,学习这些技术的关键特性.在本系列文章中,我不准备事无巨细的讨论LINQ to SQL和Entity Framework的方方面面,毕竟那样需要太多的篇幅,也会让我们从LINQ上面转移注意力,况且,园子里也有不少介绍LINQ to SQL和Entity Framework的好文章.我们在此关注的是LINQ to S

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

1. LINQ  TO SQL 和EF 特点:  LINQ TO SQL和Entity Framework都是一种包含LINQ功能的ORM 也就是所谓的关系对象的映射.其中包括的有DBFrist  Code Frist    ModeL  Frist   三种中方式 来进行和数据库之间的访问.只是形式不同而已本质没有什么区别.都是ORM  之间的转化.最终都是来进行访问底层的数据库的对象 和服务. 简单来说LINQ是为了满足不知道怎么操作数据库的程序员开发设计的,LinQ 可以让他们以一种面向数

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 framework linq to sql是一个团队 ef 是一个团队... linq to sql => ef 团队. linq to sql 适合一些小型的项目 => sqlserver ef 适合中形的项目,而且可以支持 sqllite,mysql,sqlserver 掌柜的项目开发中:使用的

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

我们知道lambda表达式在Linq to sql 和 Entity framework 中使用join函数可以实现inner join,那么怎么才能在lambda表达式中实现left join呢?秘诀就是在join后面加上一个函数DefaultIfEmpty函数,实际上这个函数在linq中貌似也只有将inner join转换为left join的作用,示例如下 var joinResult = DB.Table1s.Join(DB.Table2s, a => a.id, b => b.id,

LINQ to SQL和Entity Framework对比

LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在于EF对数据库架构和我们查询的类型实行了更好的解耦.使用EF,我们查询的对象不再是完全对应数据库架构的C#类,而是更高层的抽象:Entity Data Model.这为我们提供了额外的灵活性,但是在性能和简单性上面也会有所损失. LINQ to SQL由C#团队开发并在.NET Framework 3.5中发布,而Entity Framework由ADO.NET团队开发并作为.

LINQ to SQL和Entity Framework对比与关联

LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在于EF对数据库架构和我们查询的类型实行了更好的解耦.使用EF,我们查询的对象不再是完全对应数据库架构的C#类,而是更高层的抽象:Entity Data Model.这为我们提供了额外的灵活性,但是在性能和简单性上面也会有所损失. LINQ to SQL由C#团队开发并在.NET Framework 3.5中发布,而Entity Framework由ADO.NET团队开发并作为.

linq to sql 和 entity framework 输出sql语句

linq to sql: dbcontext.Log 属性,是一个textwriter entity framework: 1 dbcontext.Database.Log, 是一个Action<string>, eg. console.write(string) 2 after 6.1, 也可以设置 interceptor

Linq to sql 结合Entity Framework 的连接查询总结

最近在做项目使用linq结合EntityFramework来处理数据库的操作.想来也用了快一年了,发现有些使用技巧是需要注意下,特做下总结,希望对刚入门的朋友们有所帮助.刚开始用的时候各总循环查询子查询,结果性能差得不行,现在看看都觉得好笑.也只有写出过很烂的代码才知道怎么样的代码才是优雅的吧,哈哈.我先总结下连接查询吧,发现很多刚入门的朋友和我一样,不知道怎么用linq写类似与sql的连接查询(left join.inner join等等). 连接查询 内连接查询 linq语法如下: 1 va

Entity Framework 与 LINQ to SQL

Entity Framework和LINQ to SQL到底有什么区别?这是一个很常见的问题.下面的表中简要罗列了两种技术的主要区别. LINQ to SQL Entity Framework 复杂度 相对不复杂 相对比较复杂 模型 域模型(Domain model) 概念数据模型(Conceptual data model) 数据库服务器 SQL Server 多种数据库产品 开发时间 快速应用开发 需要较长时间,但支持更多特性 继承 困难 容易 文件类型 DBML文件 EDMX,CDSL,M