一步一步学Linq to sql(六):探究特性
延迟执行
IQueryable query = from c in ctx.Customers select c; |
这样的查询句法不会导致语句立即执行,它仅仅是一个描述,对应一个SQL。仅仅在需要使用的时候才会执行语句,比如:
IQueryable query = from c in ctx.Customers select c; foreach (Customer c in query) Response.Write(c.CustomerID); |
如果你执行两次foreach操作,将会捕获到两次SQL语句的执行:
IQueryable query = from c in ctx.Customers select c; foreach (Customer c in query) Response.Write(c.CustomerID); foreach (Customer c in query) Response.Write(c.ContactName); |
对应SQL:
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] 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] |
对于这样的需求,建议你先使用ToList()等方法把查询结果先进行保存,然后再对集合进行查询:
IEnumerable<Customer> customers = (from c in ctx.Customers select c).ToList(); foreach (Customer c in customers) Response.Write(c.CustomerID); foreach (Customer c in customers) Response.Write(c.ContactName); |
延迟执行的优点在于我们可以像拼接SQL那样拼接查询句法,然后再执行:
var query = from c in ctx.Customers select c; var newquery = (from c in query select c).OrderBy(c => c.CustomerID); |
DataLoadOptions
var products = from p in ctx.Products select p; foreach (var p in products) { if (p.UnitPrice > 10) ShowDetail(p.Order_Details); } private void ShowDetail(EntitySet<Order_Detail> orderdetails) {} |
由于ShowDetail方法并没有使用到订单详细信息,所以这个操作只会执行下面的SQL:
SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued] FROM [dbo].[Products] AS [t0] |
现在修改一下ShowDetail方法:
private void ShowDetail(EntitySet<Order_Detail> orderdetails) { foreach (var o in orderdetails) { Response.Write(o.Quantity + "<br>"); } } |
你会发现Linq to sql对每个价格大于10的产品都根据产品号进行了一次查询:
SELECT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t0].[Quantity], [t0].[Discount] FROM [dbo].[Order Details] AS [t0] WHERE [t0].[ProductID] = @p0 -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [1] |
这样的语句查询了N次。这样的查询不是很合理,我们可以通过设置DataContext的DataLoadOption,来指示 DataContext再加载产品信息的同时把对应的产品订单信息一起加载:
DataLoadOptions options = new DataLoadOptions(); options.LoadWith<Product>(p => p.Order_Details); ctx.LoadOptions = options; var products = from p in ctx.Products select p; 。。。。。。。。 |
再执行先前的查询会发现Linq to sql进行了左连接:
SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued], [t1].[OrderID], [t1].[ProductID] AS [ProductID2], [t1].[UnitPrice] AS [UnitPrice2], [t1].[Quantity], [t1].[Discount], ( SELECT COUNT(*) FROM [dbo].[Order Details] AS [t2] WHERE [t2].[ProductID] = [t0].[ProductID] ) AS [count] FROM [dbo].[Products] AS [t0] LEFT OUTER JOIN [dbo].[Order Details] AS [t1] ON [t1].[ProductID] = [t0].[ProductID] ORDER BY [t0].[ProductID], [t1].[OrderID] |
那么,我们怎么限制订单详细表的加载条件那?
DataLoadOptions options = new DataLoadOptions(); options.LoadWith<Product>(p => p.Order_Details); options.AssociateWith<Product>(p => p.Order_Details.Where(od => od.Quantity > 80)); ctx.LoadOptions = options; var products = from p in ctx.Products select p; |
这样,就只会有数量大于80的订单详细信息会和产品一起加载:
SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued], [t1].[OrderID], [t1].[ProductID] AS [ProductID2], [t1].[UnitPrice] AS [UnitPrice2], [t1].[Quantity], [t1].[Discount], ( SELECT COUNT(*) FROM [dbo].[Order Details] AS [t2] WHERE ([t2].[Quantity] > @p0) AND ([t2].[ProductID] = [t0].[ProductID]) ) AS [count] FROM [dbo].[Products] AS [t0] LEFT OUTER JOIN [dbo].[Order Details] AS [t1] ON ([t1].[Quantity] > @p0) AND ([t1].[ProductID] = [t0].[ProductID]) ORDER BY [t0].[ProductID], [t1].[OrderID] -- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [80] |
DataLoadOptions限制
Linq to sql对DataLoadOptions的使用是有限制的,它只支持1个1对多的关系。一个顾客可能有多个订单,一个订单可能有多个详细订单:
DataLoadOptions options = new DataLoadOptions(); options.LoadWith<Customer>(c => c.Orders); options.LoadWith<Order>(o => o.Order_Details); ctx.LoadOptions = options; IEnumerable<Customer> customers = ctx.Customers.ToList<Customer>(); |
这样的语句执行后会导致下面的SQL执行N次(参数不同):
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], [t1].[OrderID] AS [OrderID2], [t1].[ProductID], [t1].[UnitPrice], [t1].[Quantity], [t1].[Discount], ( SELECT COUNT(*) FROM [dbo].[Order Details] AS [t2] WHERE [t2].[OrderID] = [t0].[OrderID] ) AS [count] FROM [dbo].[Orders] AS [t0] LEFT OUTER JOIN [dbo].[Order Details] AS [t1] ON [t1].[OrderID] = [t0].[OrderID] WHERE [t0].[CustomerID] = @x1 ORDER BY [t0].[OrderID], [t1].[ProductID] -- @x1: Input StringFixedLength (Size = 5; Prec = 0; Scale = 0) [ALFKI] |
而对于多对1的关系,Linq to sql对于DataLoadOptions没有限制:
DataLoadOptions options = new DataLoadOptions(); options.LoadWith<Product>(c => c.Category); options.LoadWith<Product>(c => c.Order_Details); options.LoadWith<Order_Detail>(o => o.Order); ctx.LoadOptions = options; IEnumerable<Product> products = ctx.Products.ToList<Product>(); |
由于多个产品对应1个分类,多个详细订单对应1个订单,只有产品和详细订单才是多对1的关系,所以也只会有1次SQL(不过这样的操作还是少执行为妙,消耗太大了):
SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued], [t3].[OrderID], [t3].[ProductID] AS [ProductID2], [t3].[UnitPrice] AS [UnitPrice2], [t3].[Quantity], [t3].[Discount], [t4].[OrderID] AS [OrderID2], [t4].[CustomerID], [t4].[EmployeeID], [t4].[OrderDate], [t4].[RequiredDate], [t4].[ShippedDate], [t4].[ShipVia], [t4].[Freight], [t4].[ShipName], [t4].[ShipAddress], [t4].[ShipCity], [t4].[ShipRegion], [t4].[ShipPostalCode], [t4].[ShipCountry], ( SELECT COUNT(*) FROM [dbo].[Order Details] AS [t5] INNER JOIN [dbo].[Orders] AS [t6] ON [t6].[OrderID] = [t5].[OrderID] WHERE [t5].[ProductID] = [t0].[ProductID] ) AS [count], [t2].[test], [t2].[CategoryID] AS [CategoryID2], [t2].[CategoryName], [t2].[Description], [t2].[Picture] FROM [dbo].[Products] AS [t0] LEFT OUTER JOIN ( SELECT 1 AS [test], [t1].[CategoryID], [t1].[CategoryName], [t1].[Description], [t1].[Picture] FROM [dbo].[Categories] AS [t1] ) AS [t2] ON [t2].[CategoryID] = [t0].[CategoryID] LEFT OUTER JOIN ([dbo].[Order Details] AS [t3] INNER JOIN [dbo].[Orders] AS [t4] ON [t4].[OrderID] = [t3].[OrderID]) ON [t3].[ProductID] = [t0].[ProductID] ORDER BY [t0].[ProductID], [t2].[CategoryID], [t3].[OrderID] |
主键缓存
Linq to sql对查询过的对象进行缓存,之后的如果只根据主键查询一条记录的话会直接从缓存中读取。比如下面的代码:
Customer c1 = ctx.Customers.Single(customer => customer.CustomerID == "ANATR"); c1.ContactName = "zhuye"; Customer c2 = ctx.Customers.Single(customer => customer.CustomerID == "ANATR"); Response.Write(c2.ContactName); |
执行后只会产生一条SQL:
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 -- @p0: Input String (Size = 5; Prec = 0; Scale = 0) [ANATR] |
由于没有提交修改,所以数据库中的记录还是没有更新。由于这个特性,我们在使用存储过程作为实体更新方法的时候就要当心了,存储过程书写错误,即使你提交了修改也很可能导致缓存中的数据和数据库中的数据不一致,引起不必要的麻烦。
DataContext隔离
有的时候我们会把对象从外部传入DataContext,要求它更新,由于不同的DataContext是相对独立的。由于新的DataContext中还没有获取实体,我们只能通过附加方式更新数据。
首先把Customer表的主键字段加上IsVersion标识:
[Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true, IsVersion = true)] |
运行下面的测试代码:
Customer c = new Customer { CustomerID = "ALFKI", ContactName = "zhuye", CompanyName = "1111" }; ctx.Customers.Attach(c, true); ctx.SubmitChanges(); |
会捕捉到下面的SQL语句:
UPDATE [dbo].[Customers] SET [CompanyName] = @p2, [ContactName] = @p3, [ContactTitle] = @p4, [Address] = @p5, [City] = @p6, [Region] = @p7, [PostalCode] = @p8, [Country] = @p9, [Phone] = @p10, [Fax] = @p11 WHERE ([CustomerID] = @p0) AND ([CustomerID] = @p1) -- @p0: Input StringFixedLength (Size = 5; Prec = 0; Scale = 0) [ALFKI] -- @p1: Input String (Size = 5; Prec = 0; Scale = 0) [ALFKI] -- @p2: Input String (Size = 4; Prec = 0; Scale = 0) [1111] -- @p3: Input String (Size = 5; Prec = 0; Scale = 0) [zhuye] -- @p4: Input String (Size = 0; Prec = 0; Scale = 0) [] -- @p5: Input String (Size = 0; Prec = 0; Scale = 0) [] -- @p6: Input String (Size = 0; Prec = 0; Scale = 0) [] -- @p7: Input String (Size = 0; Prec = 0; Scale = 0) [] -- @p8: Input String (Size = 0; Prec = 0; Scale = 0) [] -- @p9: Input String (Size = 0; Prec = 0; Scale = 0) [] -- @p10: Input String (Size = 0; Prec = 0; Scale = 0) [] -- @p11: Input String (Size = 0; Prec = 0; Scale = 0) [] |
今天就到这里,下次讲并发与事务问题。
一步一步学Linq to sql(六):探究特性的更多相关文章
- (转载)一步一步学Linq to sql系列文章
现在Linq to sql的资料还不是很多,本人水平有限,如果有错或者误导请指出,谢谢. 一步一步学Linq to sql(一):预备知识 一步一步学Linq to sql(二):DataContex ...
- 一步一步学Linq to sql(五):存储过程
普通存储过程 首先在查询分析器运行下面的代码来创建一个存储过程: create proc sp_singleresultset as set nocount on select * from cust ...
- 步步学LINQ to SQL:为实体类添加关系【转】
[IT168 专稿]本文详细为你阐述了如何在你的应用程序中实现LINQ to SQL.附件的示例程序包括了这里探讨的所有代码,还提供了一个简单的WPF图形界面程序来显示通过数据绑定返回的结果集. 第一 ...
- 步步学LINQ to SQL:使用LINQ检索数据【转】
[IT168 专稿]该系列教程描述了如何采用手动的方式映射你的对象类到数据表(而不是使用象SqlMetal这样的自动化工具)以便能够支持数据表之间的M:M关系和使用实体类的数据绑定.即使你选择使用了自 ...
- 步步学LINQ to SQL:将类映射到数据库表【转】
[IT168 专稿]该系列教程描述了如何采用手动的方式映射你的对象类到数据表(而不是使用象SqlMetal这样的自动化工具)以便能够支持数据表之间的M:M关系和使用实体类的数据绑定.即使你选择使用了自 ...
- 一步一步学Linq to sql(四):查询句法
select 描述:查询顾客的公司名.地址信息 查询句法: var 构建匿名类型1 = from c in ctx.Customers select new { 公司名 = c.CompanyName ...
- 一步一步学Linq to sql(三):增删改
示例数据库 字段名 字段类型 允许空 字段说明 ID uniqueidentifier 表主键字段 UserName varchar(50) 留言用户名 PostTime datetime 留言时间 ...
- 一步一步学Linq to sql(二):DataContext与实体
DataContext DataContext类型(数据上下文)是System.Data.Linq命名空间下的重要类型,用于把查询句法翻译成SQL语句,以及把数据从数据库返回给调用方和把实体的修改写入 ...
- 一步一步学Linq to sql(一):预备知识
什么是Linq to sql Linq to sql(或者叫DLINQ)是LINQ(.NET语言集成查询)的一部分,全称基于关系数据的 .NET 语言集成查询,用于以对象形式管理关系数据,并提供了丰富 ...
随机推荐
- Pandas使用详细教程(个人自我总结版)
Pandas 是我最喜爱的库之一.通过带有标签的列和索引,Pandas 使我们可以以一种所有人都能理解的方式来处理数据.它可以让我们毫不费力地从诸如 csv 类型的文件中导入数据.我们可以用它快速地对 ...
- PS软件怎么把视频转成gif动态图?
PS软件怎么把视频转成gif动态图?Adobe PhotoShop软件的最新版本是可以编辑视频的,并且可以将视频转换为GIF动态图,使用也很简单,下面分享ps制作gif动态图的教程,需要的朋友可以参考 ...
- 建立自己的git账户并保存资料的重要性
Github是一个代码托管平台和开发者社区,开发者可以在Github上创建自己的开源项目并与其他开发者协作编码.创业公司可以用它来托管软件项目,开源项目可以免费托管,私有项目需付费.[2] GitHu ...
- Django运行访问项目出现的问题:Invalid HTTP_HOST header: '192.168.114.25:8001'. You may need to add u'192.168.114.25' to ALLOWED_HOSTS.
当运行python manage.py runserver 0.0.0.0:8001时候,出现Invalid HTTP_HOST header: '192.168.114.25:8001'. You ...
- codeforces 609E Minimum spanning tree for each edge
E. Minimum spanning tree for each edge time limit per test 2 seconds memory limit per test 256 megab ...
- DSL-领域特定语言(英语:domain-specific language、DSL)
领域特定语言(英语:domain-specific language.DSL)指的是专注于某个应用程序领域的计算机语言.
- python:生成器进阶
1,列表推导式 值 for 循环 2,生成器表达式 g=(i for i in range(10)) print(g) for i in g: print(i) 3,列表推导式与生成器表达式的区别 # ...
- jmeter报"msg":"Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported"的解决方法
1.报"msg":"Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supporte ...
- 【模板】各种背包问题&讲解
背包问题集合 一般来说,动态规划(DP)都是初学者最难闯过的一关,而在这里详细解说动态规划的一种经典题型:背包问题. 这里介绍的 ...
- HDU 1220 Cube(数学,找规律)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1220 Cube Time Limit: 2000/1000 MS (Java/Others) M ...