如果在C#中使用TransactionScope类(分布式事务),则须注意如下事项:
1、在项目中引用using System.Transactions命名空间(先要在添加net组件的引用);
2、具体示例如下:
/// 发送消息
/// </summary>
/// <param name="sendUserId"></param>
/// <param name="toUser">格式7FFA3AF2-E74B-4174-8403-5010C53E49A7|userName,7FFA3AF2-E74B-4174-8403-5010C53E49A7|userName</param>
/// <param name="content"></param>
/// <param name="sendedStatus">表示已送</param>
/// <returns></returns>
public static int sendMessage(string sendUserId, string toUser, string content, string sendedStatus)
{
int receiveCount = 0;
TransactionOptions transactionOption = new TransactionOptions();
//设置事务隔离级别
transactionOption.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
// 设置事务超时时间为60秒
transactionOption.Timeout = new TimeSpan(0, 0, 60);
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, transactionOption))
{
try
{
//在这里实现事务性工作
//发送消息
insertMessage(sendUserId, toUser, content, sendedStatus);
//在接收信息表中插入记录
receiveCount += insertReceiveMessage(userids[0], sendUserId, content, "0");
// 没有错误,提交事务
scope.Complete();
}
catch (Exception ex) {
throw new Exception("发送信息异常,原因:"+ex.Message);
}finally{
//释放资源
scope.Dispose();
}
}
return receiveCount;
}
3、对MSDTC组件设置:
步骤:
在控制面板--->管理工具--->服务 中,开启Distributed Transaction Coordinator 服务。
a.控制面板->管理工具->组件服务->计算机->我的电脑->右键->属性
b.选择MSDTC页, 确认"使用本地协调器"
c.点击下方"安全配置"按钮
d.勾选: "允许网络DTC访问","允许远程客户端","允许入站","允许出站","不要求进行身份验证".
e.对于数据库服务器端, 可选择"要求对呼叫方验证"
f.勾选:"启用事务Internet协议(TIP)事务"。
g.在双方防火墙中增加MSDTC.exe例外
可用命令行: netsh firewall set allowedprogram %windir%system32msdtc.exe MSDTC enable
4、重启IIS服务器
一、在TransactionScope中,如果不是必须要避免它启用DTC分布式事务,因为性能低下;而对于TransactionScope来说它是以连接对象Connection做为识别单位的,也就是说即便是相同连接字符串ConnectionString的两个连接对象Connection在TransactionScope也是会启用DTC分布式事务的,避免的方法就是在一个TransactionScope中使用一个唯一的连接对象Connection。
二、在TransactionScope中默认的事务级别是Serializable,即在事务过程中,完全性锁表。别的进程不能查询,修改,新增,删除。这样会导致效率大大降低,虽然数据完整性很高。通常我们不需要那么高的数据完整性。所以需要修改默认的事务级别:
option.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, option))
所有的事务级别如下:
Chaos 无法改写隔离级别更高的事务中的挂起的更改。
ReadCommitted 不可以在事务期间读取可变数据,但是可以修改它。
ReadUncommitted 可以在事务期间读取和修改可变数据。
RepeatableRead 可以在事务期间读取可变数据,但是不可以修改。可以在事务期间添加新数据。
Serializable 可以在事务期间读取可变数据,但是不可以修改,也不可以添加任何新数据。
Snapshot 可以读取可变数据。在事务修改数据之前,它验证在它最初读取数据之后另一个事务是否更改过这些数据。如果数据已被更新,则会引发错误。这样使事务可获取先前提交的数据值。
在尝试提升以此隔离级别创建的事务时,将引发一个 InvalidOperationException,并产生错误信息“Transactions with IsolationLevel Snapshot cannot be promoted”(无法提升具有 IsolationLevel 快照的事务)。
Unspecified 正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。如果设置了此值,则会引发异常。
一. 事务
在项目中使用事务是很常见的,这里介绍Entity Framework使用TransactionScope
添加引用 System.Transactions.dll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using ( var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew)) { var entities = new EntityDBContext(); USER_INFO userInfo = new USER_INFO() { USERNAME = "mike2" , USER_PWD = "22222" , STATUS_CODE = 0, REMARK = "EF测试" }; entities.Insert(userInfo); USER_ROLE_INFO userRoleInfo = new USER_ROLE_INFO() { ROLE_NAME = "管理员" , REMARK = "" }; entities.Insert(userRoleInfo); transactionScope.Complete(); } |
这样一个简单的事务就写好了,表USER_INFO和USER_ROLE_INFO要么都插入成功,要么都插入失败
二. 批量操作
1. 如果插入很多条数据,常规做法就是一条一条插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
using ( var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew)) { DateTime dtStart = DateTime.Now; var entities = new EntityDBContext(); for ( int i = 0; i < 2000; i++) { USER_INFO userInfo = new USER_INFO() { USERNAME = "mike_i_" + i, USER_PWD = "22222" , STATUS_CODE = 0, REMARK = "EF测试" }; entities.Insert(userInfo); } transactionScope.Complete(); } |
这里插入2000条,耗时24秒
2. 利用EntityFramework扩展插件进行批量插入
安装方法: PM> Install-Package Z.EntityFramework.Extensions -Version 3.14.0
使用介绍: http://entityframework-extensions.net/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var entities = new EntityDBContext(); List<USER_INFO> userInfolist = new List<USER_INFO>(); for ( int i = 0; i < 2000; i++) { userInfolist.Add( new USER_INFO() { USERNAME = "mike_i_" + i, USER_PWD = "22222" , STATUS_CODE = 0, REMARK = "EF批量操作" }); } //批量插入 entities.BulkInsert(userInfolist); |
同样是插入2000条,耗时834毫秒
3. 批量删除
1
2
3
4
5
|
var entities = new EntityDBContext(); //方法一 entities.BulkDelete(entities.USER_INFO.Where(q => q.USERID == 21608)); //方法二 entities.USER_INFO.Where(q => q.USERID == 21612).DeleteFromQuery(); |
4. 批量更新
1
2
3
4
5
6
7
8
9
10
11
12
|
var entities = new EntityDBContext(); var userUpdateList = entities.USER_INFO.Where(q => q.USERID == 21613).ToList(); //这里修改下需要被更新的字段 userUpdateList.ForEach(q => { q.USERNAME = "BulkUpdate更新名称" ; q.USER_PWD = "BulkUpdate更新密码" ; }); //批量更新这两个字段(方法一) entities.BulkUpdate(userUpdateList); //批量将USERNAME修改为 "21614"(方法二) entities.USER_INFO.Where(q => q.USERID == 21614).UpdateFromQuery(q => new USER_INFO() { USERNAME = "21614" }); |
三. 事务和批量操作结合使用
也是通过使用TransactionScope来使用事务,用法和上面类似
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
using ( var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew)) { var entities = new EntityDBContext(); DateTime dtStart = DateTime.Now; List<USER_INFO> userInfolist = new List<USER_INFO>(); for ( int i = 0; i < 2000; i++) { userInfolist.Add( new USER_INFO() { USERNAME = "mike_i_" + i, USER_PWD = "22222" , STATUS_CODE = 0, REMARK = "EF批量操作" }); } entities.BulkInsert(userInfolist); entities.USER_INFO.Where(q => q.USERID == 21610).UpdateFromQuery(q => new USER_INFO() { USERNAME = "test" }); transactionScope.Complete(); } |