博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linq to Sql 之延迟加载与立即加载
阅读量:6227 次
发布时间:2019-06-21

本文共 5687 字,大约阅读时间需要 18 分钟。

590 views 十二月 20, 09 by Timothy

Linq的延迟加载

Linq to Sql中默认采用的模式就是延迟执行,所谓延迟执行,其实就是在获取对象本身时,并不会获取和其关联的其他对象,只有在访问其关联对象的时候,程序才会去加载关联对象的数据到内存中。这样的好处是程序不会在初次访问的时候,就加载大批量的数据,而是以一种延迟加载的方式进行处理,相对而言,对于系统和网络的性能开支会减小很多。对于一个默认的Linq to Sql查询,延迟加载就是其默认的设置,不过,在某些情况下,延迟加载并非完全“智能”,不但没有实现其本意,反而增大了网络流量和性能开支。下面我们以SQL Server中的演示数据库NorthWind来试验一下:

 CSHARP
 
12345678910111213
LinqTestDataContext ctx =  LinqTestDataContext();ctx.Log = Console.Out; var result = ctx.Orders.Where(p => p.OrderID == 10251); foreach (var t in result){
Console.WriteLine("OrderID:" + t.OrderID + "-" + "OrderDate:" + t.OrderDate.Value.ToString("yyyy-MM-dd"));}

通过Linq to sql查询所有OrderID为10251的订单信息,并输出订单编号和订单日期。通过显示Linq的日志输出,我们可以看到后台生成的SQL语句如下:

 SQL
 
123456
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPostalCode], [t0].[ShipCountry]FROM [dbo].[Orders] AS [t0]WHERE [t0].[OrderID] = @p0

输出的SQL看来还比较正常。下面我们再来改一下我们的程序:

 

 CSHARP
 
123456789101112131415
foreach (var t in result)            {
Console.WriteLine("OrderID:" + t.OrderID + "-OrderDate:" + t.OrderDate.Value.ToString("yyyy-MM-dd") +"-CustomerName:" + t.Customer.ContactName); foreach(var m in t.Order_Details) Console.WriteLine("ProductID:" + m.ProductID + "-Price:" + m.UnitPrice + "-Amount:" + m.Quantity); }

出了输出订单相关信息外,还输出其关联对象:客户姓名、订单中的产品编号、单价、数量

再来看看输出的SQL语句:

 

 SQL
 
12345678910111213141516171819
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPostalCode], [t0].[ShipCountry]FROM [dbo].[Orders] AS [t0]WHERE [t0].[OrderID] = @p0  SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]FROM [dbo].[Customers] AS [t0]WHERE [t0].[CustomerID] = @p0  SELECT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t0].[Quantity], [t0].[Discount]FROM [dbo].[Order Details] AS [t0]WHERE [t0].[OrderID] = @p0

我们可以看到,对于我们修改后的代码,程序向数据库请求了三条SQL语句,当然,这还不是最坏的情况,但是我们在这里的确看到延迟加载似乎“变了味道”,不但没有节省开支,反而增大了网络浏览。怎样才能改善这样的情况呢?

关于立即加载

其实我们知道,有很多扩展方法会导致延迟加载失效,而开始立即执行。当我们在调用诸如:ToList、ToDictionary、ToLookup或者ToArray之类的扩展方法之后,程序会将最终的结果存放到某个临时的变量集合中,并让所有的数据一次性的加载完成。

另外,还有一种方式,通过设置DataContext的DeferredLoadingEnabled属性为false,显示的关闭默认的延迟加载方式。

 

 CSHARP
 
12
LinqTestDataContext ctx =  LinqTestDataContext();ctx.DeferredLoadingEnabled = false;

 

这些方式虽然比较方便,但是还是有一定的局限性。例如,简单的使用ToList只能解决一些简单的查询问题,而对于复杂的查询需求,ToList还是不能解决延迟取得子对象所引发的多次查询问题。并且,在大量数据被加载到内存中的时候,对内存的需求也是很大的。不过,幸好Linq to sql给我们提供了另外一套不错的方法。

使用DataLoadOptions实现对加载对象的优化

Linq to Sql提供DataLoadOptions,用以立即加载关联的对象数据,其中包含两种方法:

LoadWith方法,用于立即加载与主对象相关联的数据

AssociateWith方法,用于对关联对象的数据进行筛选,并加载

有了DataLoadOptions,我们就可以用如下的方式优化我们的查询中需要加载的对象:

 CSHARP
 
1234567891011121314151617181920212223242526
LinqTestDataContext ctx =  LinqTestDataContext();            ctx.Log = Console.Out;             DataLoadOptions dl =  DataLoadOptions();            dl.LoadWith
(p => p.Customer); dl.LoadWith
(p => p.Order_Details); ctx.LoadOptions = dl;  var result = ctx.Orders.Where(p => p.OrderID == 10251).ToList();  foreach (var t in result) {
Console.WriteLine("OrderID:" + t.OrderID + "-OrderDate:" + t.OrderDate.Value.ToString("yyyy-MM-dd") +"-CustomerName:" + t.Customer.ContactName); foreach(var m in t.Order_Details) Console.WriteLine("ProductID:" + m.ProductID + "-Price:" + m.UnitPrice + "-Amount:" + m.Quantity); }

对于我们开发者而言,需要注意的是,对于同一个DataContext实例,DataLoadOptions只能设定一次,并且一旦设定,就无法更改。

接下来,运行程序,看看优化后,程序向数据库服务器请求的的SQL:

 SQL
 
12345678910111213141516171819202122
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPostalCode], [t0].[ShipCountry], [t3].[OrderID] AS [OrderID2], [t3].[ProductID], [t3].[UnitPrice], [t3].[Quantity], [t3].[Discount], (    SELECT COUNT(*)    FROM [dbo].[Order Details] AS [t4]    WHERE [t4].[OrderID] = [t0].[OrderID]    ) AS [value], [t2].[test], [t2].[CustomerID] AS [CustomerID2], [t2].[CompanyName], [t2].[ContactName], [t2].[ContactTitle], [t2].[Address], [t2].[City], [t2].[Region], [t2].[PostalCode], [t2].[Country], [t2].[Phone], [t2].[Fax]FROM [dbo].[Orders] AS [t0]LEFT OUTER JOIN (    SELECT 1 AS [test], [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName], [t1].[ContactTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[PostalCode], [t1].[Country], [t1].[Phone], [t1].[Fax]    FROM [dbo].[Customers] AS [t1]    ) AS [t2] ON [t2].[CustomerID] = [t0].[CustomerID]LEFT OUTER JOIN [dbo].[Order Details] AS [t3] ON [t3].[OrderID] = [t0].[OrderID] WHERE [t0].[OrderID] = @p0ORDER BY [t0].[OrderID], [t2].[CustomerID], [t3].[ProductID]

可以看出,之前的分三次向数据库提交sql的情况,现在被程序优化为一条带LEFT JOIN的关联查询,而获取关联数据。三次SQL请求,被优化为一次,从而减少了数据库和网络流量开支,由此看来DataLoadOptions的好处不言而喻。

 

一点小结

延迟加载与立即加载,并无孰优孰劣之区别,在某些情况下,需要我们根据自己的需求和实际情况来选择来进行选择。

 

分享到:

 

声明: 此Blog中的文章和随笔仅代表作者在某一特定时间内的观点和结论,对其完全的正确不做任何担保或假设 

本站文章均采用  协议进行授权,除非注明,本站文章均为原创,转载请注明转自  并应以链接形式标明本文地址!

你可能也对下列文章感兴趣

转载于:https://www.cnblogs.com/ppcompany/articles/2705493.html

你可能感兴趣的文章
解决npm ERR! Unexpected end of JSON input while parsing near的方法汇总
查看>>
MySQL 入门
查看>>
js的操作及css样式
查看>>
bootstrapValidator关于js,jquery动态赋值不触发验证(不能捕获“程序赋值事件”)解决办法...
查看>>
数据库设计基础>范式
查看>>
POJ 3461 Oulipo(模式串在主串中出现的次数)
查看>>
Openstack的镜像属性
查看>>
【分享】用Canvas实现画板功能
查看>>
C++走向远洋——46(教师兼干部类、多重继承、派生)
查看>>
spring IOC源码分析(1)
查看>>
「深入理解计算系统」从Hello World开始
查看>>
手写Json转换
查看>>
Xception
查看>>
MySQL——约束(constraint)详解---转载
查看>>
模板函数
查看>>
phpcms v9实现wap单页教程
查看>>
浅析Java中的内存机制
查看>>
君子性非异也,善假于物也
查看>>
centos命令行安装mysql随机密码查看方法(遇到问题及其解决办法)
查看>>
调用WCF REST服务时,使用JSON
查看>>