以下是我的事务范围源代码的当前体系结构。第三个插件抛出.NET异常(不是SQL异常),它不会回滚前两个插入语句。我做错了什么?
编辑:我从insert2和insert3中删除了try / catch。我还从insert1 try / catch中删除了异常处理实用程序并放入“throw ex”。它仍然不会回滚事务。
编辑2:我在Insert3方法中添加了try / catch,只是在catch语句中添加了“throw”。它仍然不会回滚事务。
更新:根据我收到的反馈,“SqlHelper”类使用SqlConnection对象建立与数据库的连接,然后创建一个SqlCommand对象,将CommandType属性设置为“StoredProcedure” “并调用SqlCommand的ExecuteNonQuery方法。
我也没有将Transaction Binding = Explicit Unbind添加到当前连接字符串。我将在下一次测试中添加它。
public void InsertStuff()
{
try
{
using(TransactionScope ts = new TransactionScope())
{
//perform insert 1
using(SqlHelper sh = new SqlHelper())
{
SqlParameter[] sp = { /* create parameters for first insert */ };
sh.Insert("MyInsert1", sp);
}
//perform insert 2
this.Insert2();
//perform insert 3 - breaks here!!!!!
this.Insert3();
ts.Complete();
}
}
catch(Exception ex)
{
throw ex;
}
}
public void Insert2()
{
//perform insert 2
using(SqlHelper sh = new SqlHelper())
{
SqlParameter[] sp = { /* create parameters for second insert */ };
sh.Insert("MyInsert2", sp);
}
}
public void Insert3()
{
//perform insert 3
using(SqlHelper sh = new SqlHelper())
{
SqlParameter[] sp = { /*create parameters for third insert */ };
sh.Insert("MyInsert3", sp);
}
}
答案 0 :(得分:25)
我也遇到过类似的问题。我的问题发生是因为我在SqlCommands中使用的SqlConnection在创建TransactionScope之前已经打开,因此它从未在TransactionScope中作为事务登记。
在您输入TransactionScope块之前,SqlHelper类是否可能重用已打开的SqlConnection实例?
答案 1 :(得分:6)
看起来您正在捕获Insert3()中的异常,因此您的代码会在调用后继续。如果你想要它回滚,你需要让异常冒泡到主例程中的try / catch块,以便永远不会调用ts.Complete()语句。
答案 2 :(得分:1)
只有在不调用ts.complete的情况下退出using时才会发生隐式回滚。因为您正在处理Insert3()中的异常,所以异常永远不会导致using语句退出。
重新抛出异常或通知调用者需要回滚(将Insert3()的签名更改为bool Insert3()?)
答案 3 :(得分:1)
(基于不会吞下例外的编辑版本)
这些操作需要多长时间?如果它们中的任何一个运行时间很长,Transaction Binding bug 功能可能会咬你 - 即连接已经分离。尝试将Transaction Binding=Explicit Unbind
添加到连接字符串。
答案 4 :(得分:0)
我没有看到你的帮助程序类,但是如果你没有调用complete语句,即使你从.NET代码中得到错误,事务范围也会回滚。我为你复制了一个例子。你可能在调试时做错了什么。此示例在.net代码和类似的catch块中有错误。
private static readonly string _connectionString = ConnectionString.GetDbConnection();
private const string inserttStr = @"INSERT INTO dbo.testTable (col1) VALUES(@test);";
/// <summary>
/// Execute command on DBMS.
/// </summary>
/// <param name="command">Command to execute.</param>
private void ExecuteNonQuery(IDbCommand command)
{
if (command == null)
throw new ArgumentNullException("Parameter 'command' can't be null!");
using (IDbConnection connection = new SqlConnection(_connectionString))
{
command.Connection = connection;
connection.Open();
command.ExecuteNonQuery();
}
}
public void FirstMethod()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("@test", "Hello1"));
ExecuteNonQuery(command);
}
public void SecondMethod()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("@test", "Hello2"));
ExecuteNonQuery(command);
}
public void ThirdMethodCauseNetException()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("@test", "Hello3"));
ExecuteNonQuery(command);
int a = 0;
int b = 1/a;
}
public void MainWrap()
{
TransactionOptions tso = new TransactionOptions();
tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
//TransactionScopeOption.Required, tso
try
{
using (TransactionScope sc = new TransactionScope())
{
FirstMethod();
SecondMethod();
ThirdMethodCauseNetException();
sc.Complete();
}
}
catch (Exception ex)
{
logger.ErrorException("eee ",ex);
}
}
如果要调试事务,可以使用此脚本查看锁定和等待状态等。
SELECT
request_session_id AS spid,
CASE transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'ReadUncomitted'
WHEN 2 THEN 'Readcomitted'
WHEN 3 THEN 'Repeatable'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL ,
resource_type AS restype,
resource_database_id AS dbid,
DB_NAME(resource_database_id) as DBNAME,
resource_description AS res,
resource_associated_entity_id AS resid,
CASE
when resource_type = 'OBJECT' then OBJECT_NAME( resource_associated_entity_id)
ELSE 'N/A'
END as ObjectName,
request_mode AS mode,
request_status AS status
FROM sys.dm_tran_locks l
left join sys.dm_exec_sessions s on l.request_session_id = s.session_id
where resource_database_id = 24
order by spid, restype, dbname;
在调用异常方法之前,您将看到两个方法调用的一个SPID。
默认隔离级别是可序列化的。You can read more about locks and transactions here
答案 5 :(得分:0)
当我在TransactionScope
中调用WCF服务操作时遇到了类似的问题。
我注意到由于服务界面中的'TransactionFlow'属性,交易流程被禁止。因此,WCF服务操作未使用外部事务范围使用的事务。如下所示更改它以允许事务流可以解决我的问题。
[TransactionFlow(TransactionFlowOption.NotAllowed)]
到
[TransactionFlow(TransactionFlowOption.Allowed)]