2012-06-25 16 views
8

Ho già fatto molte ricerche ma non sono riuscito a trovare una risposta diritta.Entity Framework: come inserire più stored procedure in una transazione?

Ho due stored procedure ed entrambi la funzione sono stati importati all'oggetto DbContext

  1. INSERTA()
  2. InsertB()

voglio metterli in una transazione. (Ad esempio, se InsertB() non è riuscito, rollback InsertA())

Come faccio? Posso dichiarare un oggetto TransactionScope e racchiudere le due stored procedure?

Grazie

+0

Perché non creare una procedura involucro memorizzato che o chiama queste due procedure in una transazione o, se queste procedure devono sempre essere chiamate insieme, combina la logica in una singola procedura, in primo luogo? –

+0

Queste due procedure sono codice legacy. Ed entrambi sono enormi. – c830

+0

Ok, ma potresti ancora scrivere una procedura molto piccola che funge da wrapper. –

risposta

13

È necessario mobilitare le operazioni in un ambito della transazione, come segue:

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

    tranScope.Complete(); 
} 

In caso di errore, il campo di applicazione della transazione verranno automaticamente annullate. Naturalmente, è ancora necessario gestire le eccezioni e fare qualunque cosa detta la gestione della gestione delle eccezioni (log, ecc.). Tuttavia, se non si chiama manualmente Complete(), la transazione viene annullata quando termina l'ambito using.

L'ambito della transazione non verrà promosso a una transazione distribuita a meno che non si aprano altre connessioni di database nello stesso ambito della transazione (vedere here).

Questo è importante sapere perché altrimenti sarà necessario configurare MSDTC su tutti i server coinvolti in questa operazione (Web, livello intermedio eventualmente, server sql). Quindi, finché la transazione non viene promossa a una distribuita, starai bene.

Nota: Al fine di mettere a punto le opzioni di transazione, ad esempio timeout e livelli di isolamento, hanno uno sguardo a this TransactionScope constructor. Il livello di isolamento predefinito è serializzabile.

Esempio aggiuntivo:here.

+0

Grazie per la risposta dettagliata. – c830

4

È possibile utilizzare l'oggetto TransactionScope oppure è possibile utilizzare il metodo SqlConnection.BeginTransaction. Prestare attenzione all'utilizzo di TransactionScope, le transazioni possono essere esculate in transazioni distribuite quando si chiamano stored procedure in un database diverso. Le transazioni distribuite di possono richiedere molte risorse.

Come utilizzare SqlConnection.BeginTransaction ... (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); 
      } 
     } 
    } 
} 

Come utilizzare TransactionScope ... (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

Ma questo è un sacco di codice per qualcosa che può essere fatto con poche righe di codice, usando il corretto supporto delle transazioni. –

Problemi correlati