2011-03-04 468 views
2

我目前使用微软企业库5.0,我想知道,如果下面的代码是处理事务的接受的方式回滚多个事务。企业库:

我已经简化场景一点,但本质上,我想在同一事务内执行不同的数据库多个插入。

如果两个刀片的失败,所有的交易应该被回滚。

我已经看了TransactionScope,但我想知道如果我能没有它管理。

public void InsertStuff_AcrossDbs() 
{ 
    //Create a ref to 2 different Db's on the same server 
    Database db_a = DatabaseFactory.CreateDatabase("Data Source=localhost;Initial Catalog=db_a"); 
    Database db_b = DatabaseFactory.CreateDatabase("Data Source=localhost;Initial Catalog=db_b"); 

    //Create Connections for the 2 db's 
    using (DbConnection connection_db_a = db_a.CreateConnection()) 
    using (DbConnection connection_db_b = db_b.CreateConnection()) 
    { 
     //Create DbTransactions for the 2 db's 
     DbTransaction transaction_dbA = connection_db_a.BeginTransaction(); 
     DbTransaction transaction_dbB = connection_db_b.BeginTransaction(); 

     try 
     { 
      //Insert into DbA.Person and get the PK 
      DbCommand dbCmd_dbA_insert = db_a.GetSqlStringCommand("Insert INTO Person(Name,Age)Values('test',23); SET @pkReturnId= SCOPE_IDENTITY() "); 
      db_a.AddOutParameter(dbCmd_dbA_insert, "pkReturnId", DbType.Int32, 0); 
      db_a.ExecuteNonQuery(dbCmd_dbA_insert, transaction_dbA); 
      int personID = Convert.ToInt32(db_a.GetParameterValue(dbCmd_dbA_insert, "@pkReturnId")); 

      //Insert 'personId' into dbB.Employee (a different table in a different db) 
      DbCommand dbCmd_dbB_delete = db_a.GetSqlStringCommand("Insert INTO Employee(personId) VALUES(" + personID + ")"); 
      db_a.ExecuteNonQuery(dbCmd_dbB_delete, transaction_dbB); 

      //try to commit both transactions 
      transaction_dbA.Commit(); 
      transaction_dbB.Commit(); 
     } 
     catch (Exception ex) 
     { 
      //If either transactions fails, roll back both 
      try 
      { 
       transaction_dbA.Rollback(); 
      } 
      catch { } 

      try 
      { 
       transaction_dbB.Rollback(); 
      } 
      catch { } 

      throw ex; 
     } 
     finally 
     { 
      connection_db_a.Close(); 
      connection_db_b.Close(); 
     } 

    } 

} 

这段代码被认为OK,或者是否会有场景中不是所有的交易都将被回滚,如果他们中的一个应该抛出一个错误?

回答

3

你的代码是不是OK。有两种数据库不一致的情况。

在你的代码创建两个本地交易:一个在数据库中的一个在数据库B,这是不是你想要的,因为你想同时操作,以保持一致性一个事务中发生。要做到这一点,你需要使用分布式事务,正如你所提到的,TransactionScope是做这件事的最好方法。它也将使代码更具可读性。

例如

Database db_a = DatabaseFactory.CreateDatabase("Data Source=localhost;Initial Catalog=db_a"); 
Database db_b = DatabaseFactory.CreateDatabase("Data Source=localhost;Initial Catalog=db_b"); 

using (TransactionScope scope = new TransactionScope()) 
{ 
    using (DbConnection connA = db_a.CreateConnection()) 
    { 
     // ... 
    } 

    using (DbConnection connB = db_b.CreateConnection()) 
    { 
     // ... 
    } 

    scope.Complete(); 
} 

我知道你的代码是一个简单的场景,所以我不知道如果这些应用到实际的代码,但这里有一些其他意见:

DbCommand dbCmd_dbB_delete = 
    db_a.GetSqlStringCommand("Insert INTO Employee(personId) VALUES(" + personID + ")"); 

动态SQL字符串应该是可以避免的。由于数据库执行计划不会被其他具有不同参数的请求重复使用,因此它们容易受到SQL注入攻击以及性能下降的影响。

finally 
{ 
    connection_db_a.Close(); 
    connection_db_b.Close(); 
} 

你并不需要手动关闭连接,因为你是通过你的使用语句和关闭和处置在功能上等同配置的连接。