在.net1.1的时代,还没有TransactionScope类,因此很多关于事务的处理,都交给了SqlTransaction和 SqlConnection,每个Transaction是基于每个Connection的。这种设计对于跨越多个程序集或者多个方法的事务行为来说,不是非常好,需要把事务和数据库连接作为参数传入。
在.net2.0后,TransactionScope类的出现,大大的简化了事务的设计。示例代码如下:
static void Main(string[] args)
{
using (TransactionScope ts = newTransactionScope())
{
userBLL u = newuserBLL();
TeacherBLL t =new TeacherBLL();
u.ADD();
t.ADD();
ts.Complete();
}
}
只 需要把需要事务包裹的逻辑块写在using (TransactionScope ts = new TransactionScope())中就可以了。从这种写法可以看出,TransactionScope实现了IDispose接口。除非显示调用 ts.Complete()方法。否则,系统不会自动提交这个事务。如果在代码运行退出这个block后,还未调用Complete(),那么事务自动回 滚了。在这个事务块中,u.ADD()方法和t.ADD()方法内部都没有用到任何事务类。
TransactionScope是基于当前线程的,在当前线程中,调用Transaction.Current方法可以看到当前事务的信息。具体关于TransactionScope的使用方法,已经它的成员方法和属性,可以查看MSDN。
TransactionScope类是可以嵌套使用,如果要嵌套使用,需要在嵌套事务块中指定TransactionScopeOption参数。默认的这个参数为Required。
该参数的具体含义可以参考http://msdn.microsoft.com/zh-cn/library/system.transactions.transactionscopeoption(v=vs.80).aspx
比如下面代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static void Main(string[] args) { using (TransactionScope ts = new TransactionScope()) { Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier); userBLL u = new userBLL(); TeacherBLL t = new TeacherBLL(); u.ADD(); using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.Required)) { Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier); t.ADD(); ts2.Complete(); } ts.Complete(); } } |
当嵌套类的TransactionScope的TransactionScopeOption为Required的时候,则可以看到如下结果,他们的事务的ID都是同一个。并且,只有当2个TransactionScope都complete的时候才能算真正成功。
如果把TransactionScopeOption设为RequiresNew,则嵌套的事务块和外层的事务块各自独立,互不影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static void Main(string[] args) { using (TransactionScope ts = new TransactionScope()) { Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier); userBLL u = new userBLL(); TeacherBLL t = new TeacherBLL(); u.ADD(); using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.RequiresNew)) { Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier); t.ADD(); ts2.Complete(); } ts.Complete(); } } |
可以看到,他们的事务id是不一样的。
TransactionScopeOption设为Suppress则为取消当前区块的事务,一般很少使用。
对于多个不同服务器之间的数据库操作,TransactionScope依赖DTC(Distributed Transaction Coordinator)服务完成事务一致性。
但 是对于单一服务器数据,TransactionScope的机制则比较复杂。主要用的的是线程静态特性。线程静态特性ThreadStaticAttribute让CLR知道,它标记的静态字段的存取是依赖当前线程,而独立于其他线程的。既然存储在线程静态字段中的数据只对存储该数据的同一线程中所运行的代码可见,那么,可使用此类字段将其他数据从一个方法传递到该第一个方法所调用的其他方法,而且完全不用担心其他线程 会破坏它的工作。TransactionScope 会将当前的 Transaction 存储到线程静态字段中。当稍后实例化 SqlCommand 时(在此 TransactionScope 从线程局部存储中删除之前),该 SqlCommand 会检查线程静态字段以查找现有 Transaction,如果存在则列入该 Transaction 中。通过这种方式,TransactionScope 和 SqlCommand 能够协同工作,从而开发人员不必将 Transaction 显示传递给 SqlCommand 对象。实际上,TransactionScope 和 SqlCommand 所使用的机制非常复杂。具体可以参考文章http://www.microsoft.com/china/MSDN/library/netFramework/netframework/NETMattersSep.mspx?mfr=true
Wrox出版的《Professional C# 4 and .NET 4》也有关于TransactionScope的一些使用方法的介绍。
本文出自 “一只博客” 博客,请务必保留此出处http://cnn237111.blog.51cto.com/2359144/1271600
一、TransactionScope的优点
1、使用起来比较方便.TransactionScope可以实现隐式的事务,使你可以在写数据访问层代码的时候不用考虑到事务,而在业务层的控制事务.
2、可以实现分布式事务,比如跨库或MSMQ.
二、TransactionScope缺点
1、性价比不高.比如,你只是在"Scope"里控制一个库的事务.用"TransactionScope"就有点浪费了.
2、一般情况下只要你使用"TransactionScope",都要配置MSDTC,要配防火墙,要开139端口.这个端口不可以更改
三、如果你不得不用分布式事务,那也得琢磨琢磨
1.这步操作一定得在事务当中吗?这步操作如果没完成或者失败了,值得回滚整个事务吗?难道没有优雅的补偿措施或者容错措施?
2.分布式事务涉及到的点,必须的这么多?必须得实时的操作这一大串?不能通过通知类操作去精简掉某些点?
3.在发起分布式事务之后,你是不是做了事务无关的操作,尽管这些操作跟事务无关?(如,读取数据、计算、等用户返回消息、等其他模块的调用返回等等)要知道事务应该尽快结束。
4.你没有把一些读操作也算在事务里面了吧?这是很容易犯的错误,你在事务中Enlist了一个select 操作。
5.你的操作,某些步骤可以等全部操作完成之后再执行.这类操作具有明显的通知类特点。通知类操作是说,我给你一个通知,并且我保证通知到了你;你必须吃下这个通知,并且保证处理成功,但是你不必我一通知你你就处理。这样的操作很明显可以用另外一个任务去搞。
四、使用分布式事务注意如下几点
1:确保参与事务的machine开启了分布式事务支持;
2:如果machine开启了防火墙,需要设置msdtc进程为例外;
3:参与事务的machine不能跨域(如果跨域,目前微软还没有确切的解决方案);
4:多数据库时才使用分布式事务,如果是同一个数据库,最好使用SqlTransaction.
五、TransactionScope事务级别
在TransactionScope中默认的事务级别是Serializable,即在事务过程中,完全性锁表。别的进程不能查询,修改,新增,删除。这样会导致效率大大降低,虽然数据完整性很高。通常我们不需要那么高的数据完整性。所以需要修改默认的事务级别:
所有的事务级别如下
Chaos
无法改写隔离级别更高的事务中的挂起的更改。
ReadCommitted
不可以在事务期间读取可变数据,但是可以修改它。
ReadUncommitted
可以在事务期间读取和修改可变数据。
RepeatableRead
可以在事务期间读取可变数据,但是不可以修改。可以在事务期间添加新数据。
Serializable
可以在事务期间读取可变数据,但是不可以修改,也不可以添加任何新数据。
Snapshot
可以读取可变数据。在事务修改数据之前,它验证在它最初读取数据之后另一个事务是否更改过这些数据。如果数据已被更新,则会引发错误。这样使事务可获取先前提交的数据值。
在尝试提升以此隔离级别创建的事务时,将引发一个 InvalidOperationException,并产生错误信息“Transactionswith IsolationLevel Snapshot cannot be promoted”(无法提升具有IsolationLevel 快照的事务)。
Unspecified
正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。如果设置了此值,则会引发异常。
六、在C#中使用TransactionScope类(分布式事务),则须注意如下事项
1、在项目中引用using System.Transactions命名空间(先要在添加net组件的引用);
2、对MSDTC组件设置:
在控制面板--->管理工具--->服务 中,开启Distributed Transaction Coordinator 服务。
a、控制面板->管理工具->组件服务->计算机->我的电脑->右键->属性
b、选择MSDTC页, 确认"使用本地协调器"
c、点击下方"安全配置"按钮
d、勾选: "允许网络DTC访问","允许远程客户端","允许入站","允许出站","不要求进行身份验证".
e、对于数据库服务器端, 可选择"要求对呼叫方验证"
f、勾选:"启用事务Internet协议(TIP)事务"。
g、在双方防火墙中增加MSDTC.exe例外
可用命令行: netsh firewall set allowedprogram%windir%/system32/msdtc.exe MSDTC enable
3、重启IIS服务器。
注意:
我们只要确保数据库的打开操作是在事务范围内打开就行了。这样就可以做到事务的正确操作。
如果WEB服务器和数据库是在同一台服务器上,TransactionScope使用的是本地事务,这时不需要配置MSDTC。
如果WEB服务器和数据库不在同一台服务器上,TransactionScope会自动提升事务级别为分布式事务,这时就需要配置MSDTC。
七、TransactionScope实例
C# 代码 复制
/**////<summary>
///发送消息
///</summary>
///<paramname="sendUserId"></param>
///<param name="toUser">格式7FFA3AF2-E74B-4174-8403-5010C53E49A7|userName,7FFA3AF2-E74B-4174-8403-5010C53E49A7|userName</param>
///<paramname="content"></param>
///<param name="sendedStatus">表示已送</param>
///<returns></returns>
publicstaticint sendMessage(stringsendUserId, string toUser, stringcontent, string sendedStatus)
{
intreceiveCount = 0;
TransactionOptionstransactionOption = new TransactionOptions();
//设置事务隔离级别
transactionOption.IsolationLevel =System.Transactions.IsolationLevel.ReadCommitted;
// 设置事务超时时间为60秒
transactionOption.Timeout = new TimeSpan(0, 0, 60);
using(TransactionScope scope = newTransactionScope(TransactionScopeOption.Required, transactionOption))
{
try
{
//在这里实现事务性工作
//发送消息
insertMessage(sendUserId,toUser, content, sendedStatus);
//在接收信息表中插入记录
receiveCount +=insertReceiveMessage(userids[0], sendUserId, content, "0");
// 没有错误,提交事务
scope.Complete();
}
catch(Exception ex) {
thrownew Exception("发送信息异常,原因:"+ex.Message);
}finally{
//释放资源
scope.Dispose();
}
}
returnreceiveCount;
}
3、对MSDTC组件设置:
步骤:
在控制面板--->管理工具--->服务中,开启Distributed Transaction Coordinator 服务。
a.控制面板->管理工具->组件服务->计算机->我的电脑->右键->属性
b.选择MSDTC页, 确认"使用本地协调器"
c.点击下方"安全配置"按钮
d.勾选: "允许网络DTC访问","允许远程客户端","允许入站","允许出站","不要求进行身份验证".
e.对于数据库服务器端, 可选择"要求对呼叫方验证"
f.勾选:"启用事务Internet协议(TIP)事务"。
g.在双方防火墙中增加MSDTC.exe例外
可用命令行: netsh firewall set allowedprogram %windir%\system32\msdtc.exeMSDTC enable
由于现在的笔记本大都是64位的window7系统,然而PLSQL_Developer没有64位的,这造成了我们连接的麻烦,这里教大家怎么将 win7_oracle11g_64位连接32位PLSQL_Developer。并解决在使用PLSQL_Developer时出现中文乱码。现在开始 吧~
- 已经装好的64位Oracle数据库
- window7_64位的操作系统
- PLSQL_Developer 9.0以上版本(目前只有32位的);下面有下载连接!
- 官方的 instantclient-basic-win32-10.2.0.5 文件;下面有下载连接!
- 1
下载PLSQL_Developer地址:
http://pan.baidu.com/share/link?shareid=3768883331&uk=3557941237
下载instantclient-basic-win32-10.2.0.5地址:
http://pan.baidu.com/share/link?shareid=3782452820&uk=3557941237
- 安装你下载的PLSQL_Developer并点击汉化文件进行汉化。
安装好打开你会发现你的PLSQL_Developer不能正常连接到数据库,没有数据库的初始化内容,如图所示:
- 这时候不要着急,请将你下载的instantclient-basic-win32-10.2.0.5 文件解压。然后复制到你的数据库安装的文件夹下的producti文件夹下,我的是:
E:\app\Administrator\product。
-
然 后找到两个文件listener.ora和tnsnames.ora我的在E:\app\Administrator\product\11.2.0 \dbhome_1\NETWORK\ADMIN里面。都复制到E:\app\Administrator\product \instantclient_10_2的下面。
- 然后找到你的PLSQL_Developer安装的目录,我的是:
C:\Program Files (x86)\PLSQL Developer
在里面新建一个记事本,写入以下类似代码:
@echo off
set path=E:\app\Administrator\product\instantclient_10_2
set ORACLE_HOME=E:\app\Administrator\product\instantclient_10_2
set TNS_ADMIN=E:\app\Administrator\product\instantclient_10_2
set NLS_LANG=AMERICAN_AMERICA.AL32UTF8
start plsqldev.exe
》》》注意!!!!
我的instantclient_10_2地址是:E:\app\Administrator\product\instantclient_10_2
而你的跟我的可能不一样,所以要修改成你的instantclient_10_2的路径!
写好后,将你的记事本名称改为start.bat然后点击运行。
如果PLSQL Developer没有打开,请仔细检查你上面的步骤和代码是否正确,然后在运行start.bat。
- 打开PLSQL_Developer后,取消登陆,进入主界面。
1.点击--工具--首选项--连接:
2.把上面的注销改为提交
3.OCI库改为带有instantclient_10_2的路径。
4.应用--确定--关闭PLSQL_Developer重新运行。
- 恭喜你!现在你可以正常运行并完美的解决了win7_oracle11g_64位连接32位PLSQL_Developer的问题了!
但是,当你向数据库的表中添加中文的时候,你会因为表格中的中文都变成了乱码或者问号而苦恼和不解,抱怨自己的运气怎么这么差,觉得生活对你那么的不公平,这时候不要伤心,不要气馁,这是上帝故意制造的麻烦,让你在战胜困难的时候有更大的收获!
走!咱们继续摆正学习的心态去挑战问题!
- 首先,我们要弄清为什么会出现中文乱码。
你忘了么,咱们的数据库是64位的,而操作软件PLSQL_Developer是32位的。
也就是说你用PLSQL_Developer编写的中文是符合32位的字符集,而数据库只能正确储存是64位的中文字符集,这导致了写和读不是一种字符集,最终导致中文变成问号或乱码。
- 然后,我们找到了问题所载,现在开始解决问题!
右击我的电脑--电脑属性--高级系统设置--环境变量。
找到变量名:NLS_LANG(没有的话新建一个,有的话点击--编辑)。
将它的变量值改为:SIMPLIFIED CHINESE_CHINA.ZHS16GBK
然后点击--确定。
- 按下WIN+R(WIN就是带微软图标的那个按键),输入:regedit 打开注册表。
找到1HKEY_LOCAL_MACHINE->SOFTWARE->ORACLE->HOMEO > HKEY_LOCAL_MACHINE->SOFTWARE->ORACLE>KEY_OraDb11g_home1
找到:NLS_LANG。
右击NLS_LANG——点击——修改——数值数据改为:SIMPLIFIED CHINESE_CHINA.ZHS16GBK
最后点击--确定。
好了,重新运行你的PLSQL_Developer吧,测试一下吧,如果没有成功,根据上面步骤检查。
- 成功了吧!现在开始管理你的数据库吧!
最后,非常的恭喜你,你成功的解决问题了,首先感谢国家,再感谢百度经验,最后感谢你自己!