关于C# 中事务的写法
.net的事务分为两种,一种是企业事务,一种是数据库事务。企业事务在System.Enterprise空间中,这个多用于与外部网络接口之间的等待,它是通过建立快照(很多东西其实是不能恢复的),另一种是数据库事务。楼主用的就是数据库事务,数据库事务严格来说并不属于.net,它其实是真正的数据库事务的一种包装形式。比如SqlTransaction trans = Conn.BeginTransction();这句在执行时相当于向数据库发送了Begin trans指令,这种情况下会指导在此时的时间段内,该条连接(相当于scope)不允许执行其他的语句。换句话来说,在trans执行commit或rollback之前,该条连接只为事务独占,不可能再执行其他的命令。
换种理解思路你会更明白——conn.BeginTransction() 之后,conn在执行其命令都算是事务的一部分,直到trans提交或回滚。
SqlTransaction trans = conn.BeginTransction();
try
{
SqlCommand comm1 = Conn.CreateCommand(sqlString);
comm1.ExecuteNonQuery();
SqlCommand comm2 = new SqlCommand(sqlString2, conn)
comm2.ExecuteNonQuery();
// 提交事务
trans.Commit();
}catch
{
trans.Rollback();
}
以上语句中存在两个sqlcomm都在同一事务内执行。两个命令两种形式是等效的。
这里面有两个注意点,开始事务必须在打开的连接上,如果连接未打开的情况下是会引发错误的,也就是说conn.BeginTransction()语句执行的前替条件是conn处于open状态。所以如果能打开事务,不必要再进行conn.open()了!当然这不是楼主的错误,楼主的错误体现在另一个方面new SqlCommand(string sqltext, Connection conn, SqlTransction trans)方法的用途并不是这么用的。你代码中出现这个方法,其实new SqlCommand(string sqltext, Connection conn)如果conn在事务的标记下,该命令在执行已经做为事务的一部分了(conn标记事务后,没有提交或回滚时,该条连接上执行的所有命令都是事务命令,你可以查看代码上方的理论解释,这样有助于你的学习)。而且再使用一个trans参数传入,会被认为是另一个事务,换句话来说,这句语句在其中一个事务中执行(conn标记的事务上),同时被要求在另一个事务(传入的trans)上执行,造成了事务的并行,而这个在sql上是不被支持的,你也就是为什么你会得到一个错误“SqlCommand不支持并行事务"。
知道原因,那么修改起来也相当容易了。但这个看起来似乎对你很有用,其实呢,这相当于授人以”鱼“,这只是这个问题的解决方案,我肯定你以后不会再犯,即使如此,授人以”渔“岂不更好?
你打开了错误提示,在错误的配置节中使用了mode="on"的方式,所以你可以看到错误的,一般情况下,很多人看到这个都头大了,把这个错误直接发给”高手“,请求对方帮忙,那么这个错误为什么不自己去看下呢?所以一部分会看到这错误的提示,比如”sqlcommand不允许执行并行事务“,知道了错误的原因,很多初学的人还并不知道是什么情况下发生的错误,当然比如除0溢出一样,他只知道运行时某个分母的值是0,仅此而已。但对于排查来说也是相当困难,某个功能块中并不见得只有一个变量被做为除数的。怎么快速定位到代码呢?
堆栈跟踪起到了很大的作用:他不但有调用关系,还有错误发生的行数,它的格式就是方法(当然是方法的全名,空间名+类名+方法名)空格后跟+号和行数。这个其实是一个例外的冒泡顺序,最顶上一层是最终导致错误发生的位置(方法名称+行数),因为它的发生,所以会冒泡到调用它的方法,直接最后一个方法或try监控的方法体内。如果对类库不是很熟悉情况下,从上到下找到第一个自己写的方法,然后再查看对应的行数,就可以找到发生错误的源码位置了。可能不会理解,为什么有这么多类库中方法报错了呢?其实并不是类库错了,你调用的类库只不过是设置了他们执行方法的一些参数而已。所以是你传入的不合适的参数,并不见得是类库的错误。
比如代码下面的代码
public class Defined.Math
{
public static int Method1(int num1, int num2)
{return num1/num2;}
}
//调用方
Defined.Math.Method1(12,0);
显然调用方会发生错误,而例外的冒泡时的第一个方法是Method1的方法中的语句,而不是调用方的语句!
这就是怎么看冒泡的,虽然这种情况下,一般我们可以找到第一个我们写的方法,然后去修改它,但由于开发模式的原因,修改的未必是第一个冒泡的方法中语句。
方法教你了,再浅谈一下为什么会有这种情况,很多半瓶水的会认为修改Method1,如:
public static int Method1(int num1, int num2)
{
if(num2==0)
return 0;
return num1/num2;
}
这种代码看起来能很好的解决兼容问题,其实并非是解决问题之道。为什么呢?原因就是在于.net强大的例外机制!他们从来没有想到过会用这个强大的武器,只会用代码向上堆,有时问题并非那么简单的。如果是网络或者IO之类的错误呢?因为不会用.net的例外机制,所以他们可能没有写过这样的代码:
public void Method(int num)
{
if(num==0)
throw new Exception();
// other code doing something;
}
之所以说.net有一个强大的例外机制作为武器,是因为我们可以创建类继承于Exception实现自定的例外!那些连throw都不会用的,自然从来不会接触到自定的exception的。所以对于同的情况分析时,我们完全可能会自己抛例外,这时怎么修改?你打算把抛出例外的语句改掉吗?
当然我是拿这个例子来说明怎么定位源码中错误的位置,冒泡的第一个位置只是错误发生的位置,并不是修改代码的位置,准确理解这个含义之后,你看堆栈时,可以直接定位到错误,而不是直接定位到你写的第一个抛出例外的方法!