LINQ To SQL在N层应用程序中的CUD操作、批量删除、批量更新
原文:LINQ To SQL在N层应用程序中的CUD操作、批量删除、批量更新
0. 说明
Linq to Sql,以下简称L2S。
以下文中所指的两层和三层结构,分别如下图所示:
准确的说,这里的分层并不是特别明确:
(1) 生成的DataContext(Linq t0 SQL Runtime)和Entity是放在一个文件中的,物理上不能切割开来;上图只是展示逻辑上的结构。
(2) 拿上图右边的三层结构来说,鉴于第(1)点,UI层就可以跨越BusinessLogic层,直接访问L2S层,这可能不是我们期望的。对于这个问题,可以有如下两种办法:A.建立自己的Entity层(DomainModel,UI与BL之间使用DomainModel,BL将DomainModel转换成L2S的Entity后,再交给L2S Runtime进行数据库更新); B.告诉UI层开发人员不要访问L2S Runtime-,-
1. 生成DataContext和Entity
1.1 使用VS自带的L2S对象关系设计器(O/R设计器)
如果程序很简单只是拿来玩儿的,就少数几个表,可以直接使用此法,将表/视图/存储过程从数据源中拖拽到设计器中即可。但如果系统中的表很多,拖着拖着就把自己给拖晕了……
1.2 使用SqlMeta工具
用法见MSDN。
sqlmetal /conn:"Data Source=server;Initial Catalog=database;Persist Security Info=True;User ID=sa;Password=password" /namespace:DataEntity /code:_Entity.cs /language:csharp /context:EntityDataContext /serialization:Unidirectional /views
1.3 手工建立实体定义或者XML映射
太多活要干,如果没有特殊场景必须手工Code这堆东西的话,建议还是省省……
具体可以参考MSDN:
使用代码编辑器自定义实体类 (LINQ to SQL)
2. 两层应用下的CUD操作
两层应用下的较为简单,MSDN中有例子,借花献佛:
2.1 新增
1: Order ord = new Order
2: {
3: OrderID = 12000,
4: ShipCity = "Seattle",
5: OrderDate = DateTime.Now
6: // …
7: };
8: db.Orders.InsertOnSubmit(ord);
9: db.SubmitChanges();
2.2 更新
1: //1.查询需要修改的纪录[Lazy Load]
2: IQueryable<Order> query =
3: from ord in db.Orders
4: where ord.OrderID == 11000
5: select ord;
6: //2.修改
7: foreach (Order ord in query)
8: {
9: ord.ShipName = "Mariner";
10: ord.ShipVia = 2;
11: // Insert any additional changes to column values.
12: }
13: //3.保存
14: db.SubmitChanges();
2.3 删除
1: //1.查询需要修改的纪录[Lazy Load]
2: IQueryable<Order> query =
3: from ord in db.Orders
4: where ord.OrderID == 11000
5: select ord;
6: //2.修改
7: foreach (Order ord in query)
8: {
9: db.Orders.DeleteOnSubmit(ord);
10: }
11: //3.提交更改
12: db.SubmitChanges();
3. 多层应用下的CUD操作
新增操作同上。
但修改和删除操作则需要做调整。在文章起始位置的分层结构图中可以看到,层之间是以Entity作为数据传输对象。由于Entity的状态是有DataContext进行监控的,脱离了DataContext,则L2S无法知道对象是否做了更新、做了什么更新。譬如我的BusinessLogic被封装在一个WCF应用中,在将Entity通过网络序列化到客户端时,这些Entity会与其DataContext分离;当Entity再从客户端传回到服务器端时,服务器端使用的是一个新的DataContext,继续用上面的方法来操作的话,执行时会报错。
关于对象状态及跟踪,可以参考MSDN:对象状态与更改跟踪 (LINQ to SQL)
一种解决办法是:将上层传过来的Entity重新Attach到新的DataContext中。
3.1 新增
新增不用Attach,直接按照2.1中同样的操作方式即可。(同2.1)
3.2 删除
1: public void DeleteOrder(Order order)
2: {
3: NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString);
4:
5: db.Orders.Attach(order, false); //重新Attach一次
6: db.Orders.DeleteOnSubmit(order);
7: try
8: {
9: db.SubmitChanges(ConflictMode.ContinueOnConflict);
10: }
11: catch (ChangeConflictException e)
12: {
13: //处理更改冲突
14: }
15: }
3.3 修改
Attach可以解决删除问题,但并不能解决所有的更新问题。因为在L2S中,需要支持开放式并发检查。L2S支持以下三种开放式并发方案中的更新:(有关开放式并发的更多信息,请参见MSDN:开放式并发概述 (LINQ to SQL))
(1).基于时间戳或 RowVersion 号的开放式并发。
(2).基于实体属性子集的原始值的开放式并发。
(3).基于完整原始实体和已修改实体的开放式并发。
通俗的讲,就是如下根据如下三中处理并发的方式:
(1). 数据库表结构定义中包含时间戳(timestamp)字段:
由于时间戳的值惟一,每次更新记录时其值会同时进行更新,因此只用根据主键+时间戳,就能判断记录是否被其他人更新过;如果没有被更新过,则可以进行更新;如果被更新过,则引发ChangeConflictException异常。
时间戳字段对应的Entity定义中属性的Attribute会长成这个样子:[Column(Storage="_TimestampFieldName", AutoSync=AutoSync.Always, DbType="rowversion NOT NULL", CanBeNull=false, IsDbGenerated=true, IsVersion=true, UpdateCheck=UpdateCheck.Never)]。
此时可以按照3.1类似的方式来Attach修改后的Entity,并提交更改到数据库:(可以用Sql Server的Profile来查看生成的TSQL)
1: public void UpdateOrder(Order order)
2: {
3: NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString);
4:
5: db.Orders.Attach(order, true);
6: try
7: {
8: db.SubmitChanges();
9: }
10: catch (ChangeConflictException e)
11: {
12: //处理更改冲突
13: }
14: }
(2). 如果数据库表结构定义中不包含时间戳(timestamp)字段:
如果继续用上面(1)的方式来更新,编译时不会报错,但执行时会报错。
此时L2S不知道该咋更新了,于是就需要人为地告诉L2S该更新啥,具体包含如下两种方式
(a). 提供原始对象及更新后的属性值
1: public void UpdateOrder(Order o, short? newValue1, short? newValue2)
2: {
3: using (NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString))
4: {
5: db.Orders.Attach(o, false);//o必须是原始的没有更改过的实体对象
6:
7: o.XXOO = newValue1;
8: o.OOXX = newValue2;
9: try
10: {
11: db.SubmitChanges();
12: }
13: catch (ChangeConflictException e)
14: {
15: // 处理更改冲突
16: }
17: }
18: }
(b). 提供原始对象及更新后的对象
1: public void UpdateOrder(Order originalOrder, Order newOrder)
2: {
3: using (NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString))
4: {
5: db.Orders.Attach(newOrder, originalOrder);//originalOrder必须是原始的没有更改过的实体对象
6:
7: try
8: {
9: db.SubmitChanges();
10: }
11: catch (ChangeConflictException e)
12: {
13: // 处理更改冲突
14: }
15: }
16: }
第一种方式只需要对表增加TimeStamp类型的字段;而后面两种更新方式都需要一些额外的代码来进行处理;相比之下,还是第一种方式相对省力点儿。
如果表比较多,逐个表增加TimeStamp字段就比较讨人厌了,于是有了下面的SQL,来为所有的表增加时间戳列:
1: DECLARE @Table Table(ID int identity(1,1), TableName nvarchar(100));
2: DECLARE @Index int, @TotalCount int;
3: DECLARE @TableName nvarchar(100);
4:
5: INSERT INTO @Table(TableName)
6: SELECT TOP 100 PERCENT T.name FROM dbo.sysobjects T WHERE (T.xtype = 'u');
7: SET @TotalCount = @@RowCount;
8:
9: SET @Index = 1;
10: WHILE(@Index <= @TotalCount)
11: BEGIN
12: SELECT @TableName = TableName FROM @Table WHERE ID=@Index;
13:
14: IF(NOT Exists(SELECT 1 FROM syscolumns WHERE id = object_id(@TableName) AND number = 0
15: AND type_name(xusertype) = 'timestamp'))
16: BEGIN
17: EXEC('ALTER TABLE ' + @TableName + ' ADD [Timestamp] timestamp')
18: END
19:
20: SET @Index = @Index + 1;
21: END
尤其需要注意的是:
在尝试进行更新之前,不要将从数据库中检索数据作为一种获取原始值的方式。
4. 批量更新、批量删除
然而,上面的处理方式只是处理的最基本的CUD操作,而实际应用中的业务逻辑不会这么简单。如果涉及到稍为复杂点儿的应用,譬如我要根据一个外部传进来的条件,执行批量修改、批量删除,该咋处理呢?
关于批量删除和批量更新,老赵曾提出过自己的方案:扩展LINQ to SQL:使用Lambda Expression批量删除数据,其思路就是实现一个Expression<Func<T, bool>>解析器,并将Expression解析为最终需要执行的TSQL。在老赵的同篇英文博客中,有人在回复中提出了另一个思路,对L2S生成的TSQL进行包装,将其作为Update/Delete语句中的子查询,这样就可以直接复用L2S的查询解析器,使用L2S解析器提供的全部功能了;接下来有人将这个思路进行了实现:Batch Updates and Deletes with LINQ to SQL。原文作者写的非常详细了,并提供了源代码,可以下载回来学习或者直接复用。
1: string productName = new StringBuilder("test").Append("productName").ToString();
2: int productID = new Random().Next(10000000, Int32.MaxValue);
3: Expression<Func<Products, bool>> predicate = p => p.ProductName.Contains(productName);
4: Expression<Func<Products, Products>> evaluator =
5: p => new Products() { UnitPrice = p.UnitPrice + 1, ProductName = "Test" };
6:
7: UpdateBatch(predicate, evaluator); //批量更新
8: DeleteBatch(predicate); //批量删除
9: MultiSelect(); //批量查询
10: DeleteByPK(productID); //按主键删除
11:
12: public void UpdateBatch(Expression<Func<Products, bool>> predicate,
13: Expression<Func<Products, Products>> evaluator)
14: {
15: using (NorthWindDataContext context = new NorthWindDataContext())
16: {
17: context.Products.UpdateBatch(predicate, evaluator);
18: }
19: }
20:
21: public void DeleteBatch(Expression<Func<Products, bool>> predicate)
22: {
23: using (NorthWindDataContext context = new NorthWindDataContext())
24: {
25: context.Products.DeleteBatch(predicate);
26: }
27: }
28:
29: public void MultiSelect()
30: {
31: using (NorthWindDataContext context = new NorthWindDataContext())
32: {
33: var query1 = (from P in context.Products select P).Take(2);
34: var query2 = (from S in context.Suppliers select S).Take(2);
35: IMultipleResults result = context.SelectMutlipleResults(query1, query2);
36:
37: List<Products> products = result.GetResult<Products>().ToList();
38: List<Suppliers> suppliers = result.GetResult<Suppliers>().ToList();
39: ObjectDumper.Write(products);
40: ObjectDumper.Write(suppliers);
41: }
42: }
43:
44: public void DeleteByPK(int productID)
45: {
46: using (NorthWindDataContext context = new NorthWindDataContext())
47: {
48: context.Products.DeleteByPK(productID);
49: }
50: }
具体生成的SQL,可以用SQL Server Profile来查看。
参考《MSDN》:
1.做出和提交数据更改 (LINQ to SQL)
2.N 层应用程序中的数据检索和 CUD 操作 (LINQ to SQL)
3.开放式并发概述 (LINQ to SQL)
4.对象状态与更改跟踪 (LINQ to SQL)
LINQ To SQL在N层应用程序中的CUD操作、批量删除、批量更新的更多相关文章
- Linq to Sql:N层应用中的查询(上) : 返回自定义实体
原文:Linq to Sql:N层应用中的查询(上) : 返回自定义实体 如果允许在UI层直接访问Linq to Sql的DataContext,可以省去很多问题,譬如在处理多表join的时候,我们使 ...
- Linq to Sql:N层应用中的查询(下) : 根据条件进行动态查询
原文:Linq to Sql:N层应用中的查询(下) : 根据条件进行动态查询 如果允许在UI层直接访问Linq to Sql的DataContext,可以省去很多问题,譬如在处理多表join的时候, ...
- Linq to SQL 语法查询(链接查询,子查询 & in操作 & join,分组统计等)
Linq to SQL 语法查询(链接查询,子查询 & in操作 & join,分组统计等) 子查询 描述:查询订单数超过5的顾客信息 查询句法: var 子查询 = from c i ...
- 在Python程序中的进程操作,multiprocess.Process模块
在python程序中的进程操作 之前我们已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,刚刚我们已经了解了,运行中的程序就是一个进程.所有的进程都是通过它的父进程来创建的.因此,运行起 ...
- python 全栈开发,Day38(在python程序中的进程操作,multiprocess.Process模块)
昨日内容回顾 操作系统纸带打孔计算机批处理 —— 磁带 联机 脱机多道操作系统 —— 极大的提高了CPU的利用率 在计算机中 可以有超过一个进程 进程遇到IO的时候 切换给另外的进程使用CPU 数据隔 ...
- Python程序中的进程操作--—--开启多进程
Python程序中的进程操作-----开启多进程 之前我们已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,刚刚我们已经了解了,运行中的程序就是一个进程.所有的进程都是通过它的父进程来创 ...
- Python程序中的线程操作(线程池)-concurrent模块
目录 Python程序中的线程操作(线程池)-concurrent模块 一.Python标准模块--concurrent.futures 二.介绍 三.基本方法 四.ProcessPoolExecut ...
- sql数据查询,在程序中较慢,在MS SQL2005 Management Studio中速度快,情况分析及解决
这两天遇到一个问题,在.net开发的网站,执行sql查询,从sql profiler中监控卡看,执行时间22s. 但是拷出的sql在Management Studio中直接执行,时间仅4ms. 解决方 ...
- SQL函数TIMEDIFF在Java程序中使用报错的问题分析
需求背景 (读者可略过)司机每天从早到晚都会去到不同的自动售货机上补货,而且补货次数和路线等也是因人而异,补货依据是由系统优化并指派.但是目前系统还无法实施有效指挥和优良的补货策略,司机的补货活动因此 ...
随机推荐
- 我的第一次windows规划
#include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; //WinMain功能被分配一 ...
- 《github一天,一个算术题》:堆算法接口(堆排序、堆插入和堆垛机最大的价值,并删除)
阅览.认为.编写代码! /********************************************* * copyright@hustyangju * blog: http://blo ...
- DBA查询命令积累——不断更新
原文:DBA查询命令积累--不断更新 一.服务器配置: 1.兼容级别:兼容级别只影响指定数据库中的行为,而不会影响整个服务器上的行为. 1.1.查看数据库兼容级别及更改兼容级别: SELECT com ...
- IOS获得各种文档文件夹路径的方法
iphone沙箱模型的有四个目录,各自是什么,永久数据存储一般放在什么位置.得到模拟器的路径的简单方式是什么. documents,tmp.app,Library. (NSHomeDirectory( ...
- eclipse 中 Android sdk 无法更新的问题
诶,真是麻烦,想下个东西都下不了. 我也好久没折腾过这个了,在家的电脑是早就下载好了的,然后如今又须要下载一份.下不到.网上搜到了资料,记录下来: 第一种方法: sdk manager - ...
- UVA662- Fast Food
题意:在一条公路上,有n个酒店,要建造k个供给站(建造在酒店所在的位置),给出酒店的位置,求怎么样建造供给站才干使得每一个酒店都能得到服务且所要走的路程最短. 思路:在i到j酒店建立一个供给站,要使得 ...
- DDD领域驱动设计初探
DDD领域驱动设计初探1 前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下D ...
- 为网站添加IE6升级提示
原文 为网站添加IE6升级提示 IE6的是一款横跨十年的浏览器,作为一枚前端,对其已经失望透顶,但其在中国的市场比仍旧很高,中国大量的PC上安装的都是盗版Windows XP,而Windows XP上 ...
- CURL重试发送请求
$url1 = 'http://mk.2000tuan.com/coupon4/apiv2/getMcoupon.php; function curlGet($url) { $handle = cur ...
- 每天收获一点点------Hadoop RPC机制的使用
一.RPC基础概念 1.1 RPC的基础概念 RPC,即Remote Procdure Call,中文名:远程过程调用: (1)它允许一台计算机程序远程调用另外一台计算机的子程序,而不用去关心底层的网 ...