Dapper + Oracle + TransactionScope =交易已中止

时间:2015-04-08 02:29:17

标签: c# dapper odp.net transactionscope

我环顾四周,但找不到合适的(或对我来说是满意的)如何解决我们遇到的问题。

我使用Dapper和ODP.NET 12托管驱动程序。不使用TransactionScope时不会遇到此问题。

在事务范围下执行命令时,我通过抛出的TransactionAbortedException收到错误“Transaction has aborted”。

观察到的行为:

1)当且仅当事务完成且处理TransactionScope时才抛出TransactionAbortedException。抛出异常的时间点是处理期间。

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()

=============================================== ===================
更新(09年4月9日)

我改变了我的方法,现在使用以下模式用于Oracle。我们的代码处理Oracle和SQL Server中的连接,所以我更喜欢编码模式是一致的,但是在找到使用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();
        }
   }
}

3 个答案:

答案 0 :(得分:0)

首先,尽可能记录异常:

  

对Dispose方法的调用标记了事务范围的结束。调用此方法后发生的异常可能不会影响事务。

这是在TransactionScope类(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();
}

答案 1 :(得分:0)

我没有发现该代码有任何问题。但是,如果该循环运行的时间足够长,则事务将超时。然后,下次对数据库执行操作时,您将获得有问题的异常。我会尝试增加超时 - Timeout是TransactionScopeOption类的属性。

答案 2 :(得分:0)

我正在使用BeginTransaction()作为最终方法(请参阅我原始帖子中的更新)。我已经阅读了有关TransactionScope()失败原因的更多信息。

1)即使在连接到Oracle 10g及更低版本(source)时使用单个数据库连接,ODP.Net也会促进分布式事务。瞧,我正在连接的数据库确实是10g。

2)您需要安装Oracle MTS服务。我没有在我的开发机器上进行设置。