Dapper + Oracle + TransactionScope = 事务已中止
Dapper + Oracle + TransactionScope = Transaction has aborted
我环顾四周,但找不到合适的(或令我满意的)解决我们遇到的问题的方法。
我使用 Dapper 和 ODP.NET 12 个托管驱动程序。不使用TransactionScope时不会遇到该问题。
在事务范围内执行命令时,我通过抛出的 TransactionAbortedException 收到错误 "Transaction has aborted"。
观察到的行为:
1) 当且仅当事务完成并处理 TransactionScope 时,才会抛出 TransactionAbortedException。抛出异常的时间点是dispose.
2) 尽管有例外,交易概念确实有效!调用 Complete() 后,更改将提交到数据库中。
下面是代码片段。
// Conn string: "Data Source=OraDB;Persist Security Info=True;User ID=userxxx;Password=passwordxxx;" providerName="Oracle.ManagedDataAccess.Client
// Note: GetDbFactory().Create() returns a DbConnection object
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
using (var dbConn = GetDbFactory().Create())
{
foreach (MyDTO dto in dtoList)
{
var tableDAO= new TableDAO(dbConn);
MyEntity entity = new MyEntity()
{
Field1 = dto.Field1,
Field2 = dto.Field2
};
tableDAO.AddOrUpdate(entity);
}
// Commit changes
scope.Complete();
}
// This method is under the DAO class
public void AddOrUpdate(MyEntity entity)
{
// Verify arguments
entity.AsArgumentThrowExceptionIfNull("entity");
// build param
OracleDynamicParameters parameters = new OracleDynamicParameters();
parameters.Add("P_FIELD1", entity.Field1);
parameters.Add("P_FIELD2", entity.Field2);
// execute SP
dbConnection.Execute("PRC_MY_ENTITY_ADDORUPDATE", parameters, commandType: CommandType.StoredProcedure);
}//-- end AddOrUpdate()
=========================================== =====================
更新(2015 年 4 月 9 日)
我已经改变了我的方法,现在对 Oracle 使用以下模式。我们的代码处理 Oracle 和 SQL 服务器中的连接,所以我更希望编码模式保持一致,但在使用 Oracle+TransactionScope 找到解决方案之前,我们将使用下面的模式来执行 Oracle 命令执行:
using (var dbConnection = dbConnFactory.Create())
{
// Open db connection
dbConnection.Open();
using (var trans = dbConnection.BeginTransaction())
{
bool isSuccess = false;
try
{
// Perform DB operations here
trans.Commit();
isSuccess = true;
}
finally
{
if(!isSuccess) trans.Rollback();
}
}
}
首先,尽可能记录异常:
A call to the Dispose method marks the end of the transaction scope. Exceptions that occur after calling this method may not affect the transaction.
这在 TransactionScope class (https://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx) 的文档中。
我注意到的另一件事是在连接关闭之前完成交易。我会将其更改为关闭连接,然后完成交易。
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
{
using (var dbConn = GetDbFactory().Create())
{
foreach (MyDTO dto in dtoList)
{
var tableDAO= new TableDAO(dbConn);
MyEntity entity = new MyEntity()
{
Field1 = dto.Field1,
Field2 = dto.Field2
};
tableDAO.AddOrUpdate(entity);
}
}
// Commit changes
scope.Complete();
}
我没有发现该代码有任何问题。但是,如果该循环运行的时间足够长,事务就会超时。下次您对数据库执行操作时,您将得到有问题的异常。我会尝试增加超时 - 超时是 TransactionScopeOption class.
上的 属性
我求助于使用 BeginTransaction() 作为最终方法(请参阅我原来 post 中的更新)。我已经阅读了有关 TransactionScope() 失败原因的更多信息。
1) ODP.Net 提升为分布式事务,即使在连接到 Oracle 10g 及更低版本时使用单个数据库连接也是如此 (source)。你瞧,我要连接的数据库确实是 10g。
2) 您需要安装 Oracle MTS 服务。这个我没有在我的开发机器上设置。
我环顾四周,但找不到合适的(或令我满意的)解决我们遇到的问题的方法。
我使用 Dapper 和 ODP.NET 12 个托管驱动程序。不使用TransactionScope时不会遇到该问题。
在事务范围内执行命令时,我通过抛出的 TransactionAbortedException 收到错误 "Transaction has aborted"。
观察到的行为:
1) 当且仅当事务完成并处理 TransactionScope 时,才会抛出 TransactionAbortedException。抛出异常的时间点是dispose.
2) 尽管有例外,交易概念确实有效!调用 Complete() 后,更改将提交到数据库中。
下面是代码片段。
// Conn string: "Data Source=OraDB;Persist Security Info=True;User ID=userxxx;Password=passwordxxx;" providerName="Oracle.ManagedDataAccess.Client
// Note: GetDbFactory().Create() returns a DbConnection object
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
using (var dbConn = GetDbFactory().Create())
{
foreach (MyDTO dto in dtoList)
{
var tableDAO= new TableDAO(dbConn);
MyEntity entity = new MyEntity()
{
Field1 = dto.Field1,
Field2 = dto.Field2
};
tableDAO.AddOrUpdate(entity);
}
// Commit changes
scope.Complete();
}
// This method is under the DAO class
public void AddOrUpdate(MyEntity entity)
{
// Verify arguments
entity.AsArgumentThrowExceptionIfNull("entity");
// build param
OracleDynamicParameters parameters = new OracleDynamicParameters();
parameters.Add("P_FIELD1", entity.Field1);
parameters.Add("P_FIELD2", entity.Field2);
// execute SP
dbConnection.Execute("PRC_MY_ENTITY_ADDORUPDATE", parameters, commandType: CommandType.StoredProcedure);
}//-- end AddOrUpdate()
=========================================== =====================
更新(2015 年 4 月 9 日)
我已经改变了我的方法,现在对 Oracle 使用以下模式。我们的代码处理 Oracle 和 SQL 服务器中的连接,所以我更希望编码模式保持一致,但在使用 Oracle+TransactionScope 找到解决方案之前,我们将使用下面的模式来执行 Oracle 命令执行:
using (var dbConnection = dbConnFactory.Create())
{
// Open db connection
dbConnection.Open();
using (var trans = dbConnection.BeginTransaction())
{
bool isSuccess = false;
try
{
// Perform DB operations here
trans.Commit();
isSuccess = true;
}
finally
{
if(!isSuccess) trans.Rollback();
}
}
}
首先,尽可能记录异常:
A call to the Dispose method marks the end of the transaction scope. Exceptions that occur after calling this method may not affect the transaction.
这在 TransactionScope class (https://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx) 的文档中。
我注意到的另一件事是在连接关闭之前完成交易。我会将其更改为关闭连接,然后完成交易。
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
{
using (var dbConn = GetDbFactory().Create())
{
foreach (MyDTO dto in dtoList)
{
var tableDAO= new TableDAO(dbConn);
MyEntity entity = new MyEntity()
{
Field1 = dto.Field1,
Field2 = dto.Field2
};
tableDAO.AddOrUpdate(entity);
}
}
// Commit changes
scope.Complete();
}
我没有发现该代码有任何问题。但是,如果该循环运行的时间足够长,事务就会超时。下次您对数据库执行操作时,您将得到有问题的异常。我会尝试增加超时 - 超时是 TransactionScopeOption class.
上的 属性我求助于使用 BeginTransaction() 作为最终方法(请参阅我原来 post 中的更新)。我已经阅读了有关 TransactionScope() 失败原因的更多信息。
1) ODP.Net 提升为分布式事务,即使在连接到 Oracle 10g 及更低版本时使用单个数据库连接也是如此 (source)。你瞧,我要连接的数据库确实是 10g。
2) 您需要安装 Oracle MTS 服务。这个我没有在我的开发机器上设置。