代码改变世界

有关Transaction的错误提示及正确的处理(原创)

2006-06-28 00:31  BAsil  阅读(2299)  评论(2编辑  收藏

最近在写代码中处理事务的时候出现了几个问题,现在整理一下贴到网上。本文除特别指明外,均采用DAAB 3.1的Odbc类连接Sybase 9.1.2。

1.当分配给命令的连接处于挂起的本地事务中时,命令必须具有事务对象才能执行。该命令的 Transaction 属性尚未初始化。

问题出现的原因是Transaction并没有初始化。不过由于DAAB的Odbc类对ADO.Net的一些方法和类进行了封装,可能读者并不清楚需要何时初始化。下面我们来看一段错误的代码。


string connstr=ConfigurationSettings.AppSettings["SQLconnstr"];
OdbcConnection conn=new OdbcConnection(connstr);
conn.Open();
OdbcTransaction transaction=conn.BeginTransaction();
try
{
OdbcParameter [] searchParms = new OdbcParameter[1];
searchParms[0] = new OdbcParameter("@bh",OdbcType.VarChar);
searchParms[0].Value = "0000000001";
odbc.ExecuteNonQuery(conn,"delYsqlBxMx",searchParms);

throw new Exception("to see if the transaction rollback");

foreach(DataRow dr in BXCredDS.Tables[BaoXiaoPingZhengData.BAOXIAOMINGXI_TABLE].Rows)
{
searchParms = new OdbcParameter[4];
searchParms[0]=new OdbcParameter("@bh",OdbcType.VarChar);
searchParms[1] = new OdbcParameter("@xh",OdbcType.Int);
searchParms[2]=new OdbcParameter("@code",OdbcType.VarChar);
searchParms[3]=new OdbcParameter("@je",OdbcType.Double);

searchParms[0].Value="0000000001";
searchParms[1].Value=Convert.ToInt32(dr[BaoXiaoPingZhengData.XUHAO_FIELD]);
searchParms[2].Value=dr[BaoXiaoPingZhengData.CODE_FIELD].ToString();
searchParms[3].Value=Convert.ToDecimal(dr[BaoXiaoPingZhengData.SUM_FIELD]);

odbc.ExecuteNonQuery(conn,"insertYsqlBxMx",searchParms);
}


transaction.Commit();
conn.Close();
}
catch(Exception ex)
{
transaction.Rollback();
conn.Close();
throw ex;
}


这段代码运行后会出现上面的错误,通过调试发现,ExecuteNonQuery方法内部创建的Command对象的Transaction属性并没有赋值,因此我们需要这样写

odbc.ExecuteNonQuery(transaction,"delYsqlBxMx",searchParms);

2.无提示错误,但事务没有执行。(不易发现)


string connstr=ConfigurationSettings.AppSettings["SQLconnstr"];
OdbcConnection conn=new OdbcConnection(connstr);
conn.Open();
OdbcTransaction transaction=conn.BeginTransaction();
try
{
OdbcParameter [] searchParms = new OdbcParameter[1];
searchParms[0] = new OdbcParameter("@bh",OdbcType.VarChar);
searchParms[0].Value = "0000000001";
odbc.ExecuteNonQuery(connstr,"delYsqlBxMx",searchParms);

throw new Exception("to see if the transaction rollback");

foreach(DataRow dr in BXCredDS.Tables[BaoXiaoPingZhengData.BAOXIAOMINGXI_TABLE].Rows)
{
searchParms = new OdbcParameter[4];
searchParms[0]=new OdbcParameter("@bh",OdbcType.VarChar);
searchParms[1] = new OdbcParameter("@xh",OdbcType.Int);
searchParms[2]=new OdbcParameter("@code",OdbcType.VarChar);
searchParms[3]=new OdbcParameter("@je",OdbcType.Double);

searchParms[0].Value="0000000001";
searchParms[1].Value=Convert.ToInt32(dr[BaoXiaoPingZhengData.XUHAO_FIELD]);
searchParms[2].Value=dr[BaoXiaoPingZhengData.CODE_FIELD].ToString();
searchParms[3].Value=Convert.ToDecimal(dr[BaoXiaoPingZhengData.SUM_FIELD]);

odbc.ExecuteNonQuery(connstr,"insertYsqlBxMx",searchParms);
}


transaction.Commit();
conn.Close();
}
catch(Exception ex)
{
transaction.Rollback();
conn.Close();
throw ex;
}


这种情况,由于同样没有传入Transaction,事务回滚并没有执行,但是奇怪的是,这种情况没有报任何的错误。改正方法同1。

3.使用SqlCommandBuilder.DeriveParameters方法出现"当分配给命令的连接处于挂起的本地事务中时,命令必须具有事务对象才能执行。该命令的 Transaction 属性尚未初始化"。使用框架类库SqlClient命名空间下的类。来看一下错误的代码。


string connstr="server=localhost;database=XTERPCURBS;uid=sa;pwd=;";
SqlConnection conn=new SqlConnection(connstr);
conn.Open();
SqlTransaction tx = conn.BeginTransaction();

SqlCommand Comm = new SqlCommand();

Comm.CommandType = CommandType.StoredProcedure;
try
{
Comm.Connection = conn;
Comm.CommandText = "insertYsqlBxMx";
Comm.Transaction = tx;
SqlCommandBuilder.DeriveParameters(Comm);

tx.Commit();
}
catch(Exception ex)
{
tx.Rollback();
throw new Exception();
}


原因是SqlCommandBuilder.DeriveParameters方法不支持事务。可能有的读者会想到为什么DAAB 3.1的SqlServer类中ExecuteNonQuery(string connectionString, string spName, params object[] parameterValues)可以支持事务,我们来看一下SqlServer类的ExecuteNonQuery的代码,其中并没有用到 SqlCommandBuilder.DeriveParameters(IDbCommand cmd)方法而是使用了DAAB 3.1的类SqlDeriveParameters.DeriveParameters(IDbCommand cmd)。来看一下SqlDeriveParameters,DAAB 3.1创建这个类的解释是"We create our own class to do this because the existing ADO.NET 1.1 implementation is broken.",也就是说SqlCommandBuilder存在bug。对于Odbc的连接,DAAB 3.1仍然采用和SqlCommandBuilder类似的OdbcCommandBuilder。但我在测试中发现,OdbcCommandBuilder在Sql Server 2000下可以正常工作,而在Sybase 9.1.2下却无法取得存储过程的参数及其数据类型(参见我的另一篇文章使用DAAB 3.1连接Sybase ASE 11.9.2数据库的两个问题),对于事务没有测试,个人感觉应该能够支持。

P.S.罗嗦了这么多,终于写完了,现在巴西2:0加纳,下半场15分钟,看来巴西进8强应该没什么问题了。看球去了!