我正在ASP.Net中执行提交例程。问题是,在try-catch块中调试代码时,如果I / user遇到错误,SQL事务永远不会回滚。
如果我在其间中断这个提交例程,SQL Server 2008完全挂起。我甚至无法从SSMS执行选择/插入操作。最后,我必须重新启动SQL Server才能回滚事务。
提交代码:
SqlConnection conn = Db.getConn();
if (conn.State == ConnectionState.Closed) conn.Open();
SqlTransaction trn;
trn = conn.BeginTransaction();
SqlCommand sqlCmd = new SqlCommand("", conn);
sqlCmd.Transaction = trn;
try
{
string query = GetQuery(); // works fine
sqlCmd.CommandText = query;
sqlCmd.ExecuteNonQuery();
using (SqlBulkCopy bcp = new SqlBulkCopy(conn,SqlBulkCopyOptions.Default, trn))
{
bcp.ColumnMappings.Add("FaYear", "FaYear");
bcp.ColumnMappings.Add("CostCode", "CostCode");
bcp.ColumnMappings.Add("TokenNo", "TokenNo");
bcp.DestinationTableName = "ProcessTokenAddress";
bcp.WriteToServer(globaltblAddress.DefaultView.ToTable());
}
trn.commit();
}
catch (SqlException ex)
{
trn.Rollback();
}
注意:在这里编写代码时,我意识到我已经捕获 SqlException 而不是异常。这是导致错误的原因吗?唷?
重要事项:我是否需要在Page_UnLoad或其他可处理意外情况的事件处理程序中回滚事务(例如,用户在事务进行时关闭浏览器,用户点击后退按钮等)。
答案 0 :(得分:1)
首先,在.Net中,您不应该维护一次性重复使用的单一开放连接。这样做的结果实际上正是您所经历的 - 在连接应该关闭的情况下,它不是。
其次,连接实现IDisposable
。这意味着应该在using
语句或try-catch
块中创建和使用它们,其中finally
明确关闭连接。如果你有一个自己实现IDisposable
的类并且在它自己的生命周期内保持连接,那么你就可以打破这个规则,然后在处理它时关闭它。
您可能会想到通过不打开和关闭连接来提高效率。事实上,你错了,因为.Net为你处理连接池。标准做法是让您传递连接字符串,而不是打开连接对象。您可以将连接字符串包装在将为您返回新连接的类中,但是您不应该保持打开的连接。这样做可能会导致您遇到的错误。
相反,做这些事情:
使用using
语句。这将在创建和使用它们之后正确清理您的连接。
using (SqlConnection conn = Db.getConn()) {
conn.Open();
// your code here
}
您必须检查连接是否打开这一事实表明问题。不要这样做。相反,请更改Db
类中的代码,以便每次都分发新创建的连接。然后你可以确定国家将被关闭,你可以自信地打开它。或者,在Db
类中打开连接,但为方法命名以指示连接将打开,例如GetNewOpenConnection
。 (尽量避免在方法和类名中使用缩写。)
我建议您throw
出错。虽然记录它而不是抛出它是一种可能的选择,但是在用户坐在计算机上预期结果的任何环境中,简单地吞下错误将不是正确的操作,因为那样你的后来的代码将如何知道发生了错误让用户知道?有必要将异常信息传递给用户。最好不要处理异常,而不是静静地吞下它。
最后,一个小小的风格是getConn()
不遵循C#社区中发现的并且由Microsoft推荐的常规大小写实践。类中的公共方法应以大写字母开头:GetConn()
。