2012-06-25 5 views
8

Ich habe schon viel gesucht, konnte aber keine gerade Antwort finden.Entity Framework: Wie werden mehrere gespeicherte Prozeduren in eine Transaktion eingefügt?

Ich habe zwei gespeicherte Prozeduren und sie beide Funktion wurden dem DbContext Objekt importiert

  1. INSERTA()
  2. InsertB()

Ich möchte, dass sie in einer Transaktion setzen. (d. h. wenn InsertB() fehlgeschlagen ist, Rollback-InsertA())

Wie mache ich das? Kann ich einfach ein TransactionScope-Objekt deklarieren und die beiden gespeicherten Prozeduren umschließen?

Dank

+0

Warum Sie nicht über einen Wrapper gespeicherte Prozedur erstellen, die entweder diese beiden Verfahren in einer Transaktion aufruft oder, Wenn diese Prozeduren immer zusammen aufgerufen werden sollen, verbindet sie die Logik zu einem einzigen Vorgang? –

+0

Diese beiden Verfahren sind Legacy-Code. Und sie beide riesig. – c830

+0

Okay, aber Sie könnten immer noch eine sehr kleine Prozedur schreiben, die als Wrapper fungiert. –

Antwort

13

Sie benötigen Operationen in einem Transaktionsbereich zu gewinnen, wie folgt:

using(TransactionScope tranScope = new TransactionScope()) 
{ 
    InsertA(); 
    InsertB(); 

    tranScope.Complete(); 
} 

Bei einem Fehler wird der Transaktionsbereich automatisch zurückgesetzt werden. Natürlich müssen Sie immer noch mit Ausnahmen umgehen und alles tun, was Ihr Ausnahmebehandlungsentwurf vorschreibt (Protokoll usw.). Wenn Sie jedoch Complete() nicht manuell aufrufen, wird die Transaktion zurückgesetzt, wenn der Bereich using endet.

Der Transaktionsbereich wird nicht in eine verteilte Transaktion hochgestuft, es sei denn, Sie öffnen andere Datenbankverbindungen im selben Transaktionsbereich (siehe here).

Dies ist wichtig zu wissen, weil sonst müssten Sie MSDTC auf all Ihren Servern in diesem Betrieb (Web, Middle Tier schließlich, SQL-Server) zu konfigurieren. Solange also die Transaktion nicht auf eine verteilte übertragen wird, geht es Ihnen gut.

Hinweis: Um Ihre Transaktion Optionen zur Feinabstimmung, wie Timeouts und Isolationsstufen, haben einen Blick auf this TransactionScope constructor. Die Standardisolationsstufe ist serialisierbar.

Zusätzliche Probe:here.

+0

Danke für die ausführliche Antwort. – c830

4

Sie können das TransactionScope-Objekt verwenden oder die SqlConnection.BeginTransaction-Methode verwenden. Seien Sie vorsichtig bei der Verwendung von TransactionScope, Transaktionen können zu verteilten Transaktionen verarbeitet werden, wenn gespeicherte Prozeduren in einer anderen Datenbank aufgerufen werden. Verteilte Transaktionen können ressourcenintensiv sein.

Wie SqlConnection.BeginTransaction verwenden ... (http://msdn.microsoft.com/en-us/library/86773566.aspx)

private static void ExecuteSqlTransaction(string connectionString) 
{ 
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    { 
     connection.Open(); 

     SqlCommand command = connection.CreateCommand(); 
     SqlTransaction transaction; 

     // Start a local transaction. 
     transaction = connection.BeginTransaction("SampleTransaction"); 

     // Must assign both transaction object and connection 
     // to Command object for a pending local transaction 
     command.Connection = connection; 
     command.Transaction = transaction; 

     try 
     { 
      command.CommandText = 
       "Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')"; 
      command.ExecuteNonQuery(); 
      command.CommandText = 
       "Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')"; 
      command.ExecuteNonQuery(); 

      // Attempt to commit the transaction. 
      transaction.Commit(); 
      Console.WriteLine("Both records are written to database."); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Commit Exception Type: {0}", ex.GetType()); 
      Console.WriteLine(" Message: {0}", ex.Message); 

      // Attempt to roll back the transaction. 
      try 
      { 
       transaction.Rollback(); 
      } 
      catch (Exception ex2) 
      { 
       // This catch block will handle any errors that may have occurred 
       // on the server that would cause the rollback to fail, such as 
       // a closed connection. 
       Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType()); 
       Console.WriteLine(" Message: {0}", ex2.Message); 
      } 
     } 
    } 
} 

Wie Transaction verwenden ... (http : //msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx)

// This function takes arguments for 2 connection strings and commands to create a transaction 
// involving two SQL Servers. It returns a value > 0 if the transaction is committed, 0 if the 
// transaction is rolled back. To test this code, you can connect to two different databases 
// on the same server by altering the connection string, or to another 3rd party RDBMS by 
// altering the code in the connection2 code block. 
static public int CreateTransactionScope(
    string connectString1, string connectString2, 
    string commandText1, string commandText2) 
{ 
    // Initialize the return value to zero and create a StringWriter to display results. 
    int returnValue = 0; 
    System.IO.StringWriter writer = new System.IO.StringWriter(); 

    try 
    { 
     // Create the TransactionScope to execute the commands, guaranteeing 
     // that both commands can commit or roll back as a single unit of work. 
     using (TransactionScope scope = new TransactionScope()) 
     { 
      using (SqlConnection connection1 = new SqlConnection(connectString1)) 
      { 
       // Opening the connection automatically enlists it in the 
       // TransactionScope as a lightweight transaction. 
       connection1.Open(); 

       // Create the SqlCommand object and execute the first command. 
       SqlCommand command1 = new SqlCommand(commandText1, connection1); 
       returnValue = command1.ExecuteNonQuery(); 
       writer.WriteLine("Rows to be affected by command1: {0}", returnValue); 

       // If you get here, this means that command1 succeeded. By nesting 
       // the using block for connection2 inside that of connection1, you 
       // conserve server and network resources as connection2 is opened 
       // only when there is a chance that the transaction can commit. 
       using (SqlConnection connection2 = new SqlConnection(connectString2)) 
       { 
        // The transaction is escalated to a full distributed 
        // transaction when connection2 is opened. 
        connection2.Open(); 

        // Execute the second command in the second database. 
        returnValue = 0; 
        SqlCommand command2 = new SqlCommand(commandText2, connection2); 
        returnValue = command2.ExecuteNonQuery(); 
        writer.WriteLine("Rows to be affected by command2: {0}", returnValue); 
       } 
      } 

      // The Complete method commits the transaction. If an exception has been thrown, 
      // Complete is not called and the transaction is rolled back. 
      scope.Complete(); 

     } 

    } 
    catch (TransactionAbortedException ex) 
    { 
     writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message); 
    } 
    catch (ApplicationException ex) 
    { 
     writer.WriteLine("ApplicationException Message: {0}", ex.Message); 
    } 

    // Display messages. 
    Console.WriteLine(writer.ToString()); 

    return returnValue; 
} 
+0

Aber das ist eine Menge Code für etwas, das mit nur wenigen Codezeilen erledigt werden kann, indem die richtige Transaktionsunterstützung verwendet wird. –

Verwandte Themen