SqlTransaction 导致应用程序因错误而崩溃?
以下是一些背景信息: 我们的网站会定期崩溃,以至于必须重新启动 IIS;这几乎总是在修补 DLL 后的一个小时内发生(我们使用网站项目,而不是 Web 应用程序项目,因此每个 ASPX 页面都是一个单独的 DLL)。
在做一些研究时,我发现我们的自制 DAL 可以在调试时导致 Visual Studio 的内置 Web 服务器实际上停止工作并在存储过程中遇到 SQL 错误时关闭(我的意思是它不仅会抛出一个在浏览器中显示的异常,它实际上会说 Web 服务器遇到了错误并且需要关闭!)
在进一步挖掘中,该错误似乎与对所有内容(包括 Select 语句)使用事务有关达尔。似乎发生的情况是这样的:
- 尝试执行存储过程,存储过程由于缺少/无效列或其他错误而失败。
- 应用程序代码捕获错误并重新抛出它(不好,是的,但我没有写这个)。
- 尽管发生异常,事务仍尝试提交,在
transaction.Commit()
行上获取NullReferenceException
(似乎位于Connection
属性上,因为有是交易对象)。另外,这个 NullRef 似乎无法被捕获(我尝试了一个演示,该演示因无效的 Sproc 而强制崩溃,并且 NullRef 从未被捕获,即使输出错误的类型为 System.NullReferenceException ) - 事务抛出错误上面写着“交易已完成且不再可用”。
- ???但是 VS Web 服务器崩溃了。调试这部分似乎挂在上面的异常上,永远不会离开该方法。
现在,我不知道这是否是导致 IIS 崩溃的原因,但它看起来很可疑,而且无论如何这都是一个明显的错误。
之前没有处理过事务并且只了解它们的基本概念,我的第一个问题是为什么事务在抛出异常后仍然尝试提交?我的第二个问题是如何修复失败的提交以及可能无限循环的异常,直到服务器崩溃。添加这样的内容(该方法采用名为 transaction
的 SqlTransaction 参数)是否有意义:
catch (SqlException se)
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
这个小更改是否可以修复我认为导致 IIS 崩溃的持续异常循环? DAL 本身非常脆弱,并且在数百个文件中具体使用,因此我无法正确地从头开始重写它。
编辑整个代码块是这样的(同样,遗留代码 - 使用旧的微软数据访问块助手):
public static DataSet ExecuteDatasetStoredProc(SqlConnection conn, String storedProcName, SqlTransaction transaction, params SqlParameter[] storedProcParms)
{
try
{
// Execute the stored proc
if (transaction != null)
{
return SqlHelper.ExecuteDataset(transaction, CommandType.StoredProcedure, storedProcName, storedProcParms);
}
else
{
return SqlHelper.ExecuteDataset(conn, CommandType.StoredProcedure, storedProcName, storedProcParms);
}
}
catch (SqlException se)
{
throw new ApplicationException("Error calling " + storedProcName + ". " + se.Message, se);
}
}
但是,如果catch块执行事务仍然尝试提交,这似乎会导致挂断。
Here's some background: Periodically our site will crash to the point of having to restart IIS; this almost always happens within an hour of patching a DLL (we use a Web Site project, not a Web Application project, so each ASPX page is a separate DLL).
In doing some research I have found that our homebrew DAL can, while debugging, cause the built-in webserver with Visual Studio to actually stop working and be shut down if it encounters a SQL error in a stored procedure (I mean it will not only throw an exception that is displayed in the browser, it will actually say that the web server has experienced an error and needs to close!)
In digging further the error seems to be related to the use of transactions for everything (including Select statements) in the DAL. What seems to happen is this:
- Tries to execute stored procedure, stored procedure fails due to missing/invalid column or other error.
- Application code catches the error and rethrows it (bad, yes, but I didn't write this).
- Transaction tries to commit despite the exception, gets a
NullReferenceException
on thetransaction.Commit()
line (seems to be on theConnection
property because there is a transaction object). Also this NullRef seems like it cannot be caught (I tried a demo that force crashed with an invalid Sproc and the NullRef was never caught even though outputting the error gave its type asSystem.NullReferenceException
) - Transaction throws error that say something like "The transaction has completed and is no longer usable".
- ??? but the VS web server crashes. Debugging this part seems to hang on the above exception, never leaving the method.
Now, I don't know if this is what causes IIS to crash, but it seems quite suspicious and it's a glaring error in any event.
Having not dealt with transactions before and having only the basic idea of them, my first question is why the transaction is still trying to commit after an exception is being thrown? My second question is how to fix the failing commit and presumably infinite looping of exceptions until the server dies. Wouldn't it make sense to add something like this (the method takes a SqlTransaction parameter named transaction
):
catch (SqlException se)
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
Would that small change fix the constant exception loop that I think is crashing IIS? The DAL itself is extremely brittle and is used concretely in hundreds of files so I can't rewrite it from scratch correctly.
EDIT The entire code block is this (again, legacy code - uses the old microsoft data access block helper):
public static DataSet ExecuteDatasetStoredProc(SqlConnection conn, String storedProcName, SqlTransaction transaction, params SqlParameter[] storedProcParms)
{
try
{
// Execute the stored proc
if (transaction != null)
{
return SqlHelper.ExecuteDataset(transaction, CommandType.StoredProcedure, storedProcName, storedProcParms);
}
else
{
return SqlHelper.ExecuteDataset(conn, CommandType.StoredProcedure, storedProcName, storedProcParms);
}
}
catch (SqlException se)
{
throw new ApplicationException("Error calling " + storedProcName + ". " + se.Message, se);
}
}
However, if the catch block executes the transaction still tries to commit and this seems to be causing the hangups.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您将事务代码包装在 try catch 中,也请更改您的
also Change your if you wrap your Transactional code in a try catch