为什么即使从未调用 TransactionScope.Complete() 也会提交嵌套事务?
我正在测试嵌套事务如何工作,并发现了这种令人不安和意外的行为。
using(TransactionScope otx = new TransactionScope())
using(SqlConnection conn1 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
using(SqlCommand cmd1 = conn1.CreateCommand())
{
conn1.Open();
cmd1.CommandType = CommandType.Text;
cmd1.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (1,0x)";
cmd1.ExecuteNonQuery();
using(TransactionScope itx = new TransactionScope(TransactionScopeOption.RequiresNew))
using(SqlConnection conn2 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
using(SqlCommand cmd2 = conn1.CreateCommand())
{
conn2.Open();
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (2,0x)";
cmd2.ExecuteNonQuery();
// we don't commit the inner transaction
}
otx.Complete(); // nonetheless, the inner transaction gets committed here and two rows appear in the database!
}
我看到这个其他问题,但是解决方案不适用。
如果我不指定TransactionScopeOption.RequiresNew(即我不使用嵌套事务,仅使用嵌套作用域),那么当内部作用域未完成时整个事务会回滚,并且在调用 otx.Complete 时会发生错误()。这很好。
但我当然不希望嵌套事务在未成功完成时被提交!有谁知道这里发生了什么以及我如何才能获得预期的行为?
数据库是SQL Server 2008 R2。
I was testing to see how nested transactions work, and uncovered this disturbing and unexpected behavior.
using(TransactionScope otx = new TransactionScope())
using(SqlConnection conn1 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
using(SqlCommand cmd1 = conn1.CreateCommand())
{
conn1.Open();
cmd1.CommandType = CommandType.Text;
cmd1.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (1,0x)";
cmd1.ExecuteNonQuery();
using(TransactionScope itx = new TransactionScope(TransactionScopeOption.RequiresNew))
using(SqlConnection conn2 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
using(SqlCommand cmd2 = conn1.CreateCommand())
{
conn2.Open();
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (2,0x)";
cmd2.ExecuteNonQuery();
// we don't commit the inner transaction
}
otx.Complete(); // nonetheless, the inner transaction gets committed here and two rows appear in the database!
}
I saw this other question, but the solution did not apply.
If I don't specify TransactionScopeOption.RequiresNew (i.e. I don't use a nested transaction, just a nested scope), then the entire transaction is rolled back when the inner scope is not completed, and an error occurs when calling otx.Complete(). This is fine.
But I certainly don't expect a nested transaction to be committed when it did not complete successfully! Does anybody know what is going on here and how I can get the expected behavior?
The database is SQL Server 2008 R2.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,有 SQL Server 中没有嵌套事务。这很重要。
其次,两个 TransactionScope 都使用 conn1,因此您(在 SQL Server 级别)为每个
BEGIN TRANSACTION
递增@@TRANCOUNT
简单解释:当外部事务提交时,内部事务被提交提交,因为回滚内部事务会回滚两个个事务
,即
COMMIT TRANSACTION
(由.Complete
和.Dispose
隐含) >) 递减@@TRANCOUNT
,而ROLLBACK TRANSACTION
(仅由.Dispose
隐含)将其恢复为零。因此,由于“没有嵌套事务之类的东西”,内部回滚被抑制。如果您在内部范围中正确使用了 conn2 ,它将按预期工作,因为这两个事务在数据库服务器级别上是不相关的。这就是重要的地方...
First off, there is no such thing as a nested transaction in SQL Server. This is important.
Second, both TransactionScopes use conn1 so you are (at the SQL Server level) incrementing
@@TRANCOUNT
for eachBEGIN TRANSACTION
Simple explanation: the inner transaction is committed when the outer transaction commits because rolling back the inner would rollback both transactions
That is,
COMMIT TRANSACTION
(implied by.Complete
and.Dispose
) decrements@@TRANCOUNT
whileROLLBACK TRANSACTION
(implied by.Dispose
only) takes it back to zero. So the inner rollback is suppressed because of "no such thing as nested transactions"If you'd used conn2 correctly in the inner 'scope it would work as expected because the 2 transactions are unrelated at the database server level. Which is where it matters...
您的第二个 Command 对象是在
conn1
上创建的,而不是在conn2
上创建的,因此它非常类似于另一个问题 - 您运行命令的连接在第二个交易范围被开放。Your second Command object is being created on
conn1
, notconn2
, so it's very much like the other question - the connection on which you're running the command was opened before the second transaction scope was opened.