2015-07-02 9 views
8

Sto utilizzando EF 6 con un database Azure Sql. Secondo Microsoft, utenti avviate le transazioni non sono supportate (ref: https://msdn.microsoft.com/en-us/data/dn307226#transactions)Transazione per ExecuteSqlCommand in Azure

Ora, con EF 6, ExecuteSqlCommand è avvolto in una transazione di default:

Partendo EF6 Database.ExecuteSqlCommand() per impostazione predefinita avvolgerà il comando in una transazione se uno non era già presente. (Ref: https://msdn.microsoft.com/en-us/data/dn456843.aspx)

Dato il mio scenario, dovrei sempre sopprimere ExecuteSqlCommand comportamento transazionale in questo modo:

context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, @"TRUNCATE TABLE Person;"); 

risposta

6

Questa affermazione che ci si riferisce a si applica solo per ritentare strategie:

Dopo aver configurato una strategia di esecuzione che risulta in tentativi ...

L'articolo a cui è collegato non è specifico di Azure. Il database SQL di Azure supporta le transazioni.

+0

Forse sto fraintendendo, ma non è consigliabile utilizzare una strategia di esecuzione di nuovo tentativo quando si lavora con un database SQL di Azure? In quanto tale, se utilizzo una strategia del genere, c'è qualche preoccupazione riguardo al comportamento predefinito di "ExecuteSqlCommand', o sono semplicemente paranoico e sto leggendo male le cose? –

+0

Il tentativo deve essere eseguito a livello di transazione. Per questo motivo ritengo il supporto per i tentativi in ​​EF piuttosto inutile. EF non può riprovare la transazione, può riprovare una singola query o la chiamata SaveChanges. È il livello sbagliato per riprovare. Se si lasciano le transazioni di controllo EF, verrà utilizzata una transazione per query/salvataggio. Le strategie di riprova lo supportano. Ma ti consiglio di usare le transazioni corrette e riprovare (magari usando un semplice metodo di supporto) .; EF può avvolgere un ciclo retry attorno al proprio codice. Ma non può avvolgere un ciclo di tentativi intorno al * codice *. Quindi ogni singola chiamata a EF può essere ripetuta, ma non è possibile effettuare più chiamate. – usr

3

Se utilizzare o meno TransactionalBehavior.DoNotEnsureTransaction dipende dal fatto che si desidera che ci sia una transazione durante l'ambito del comando. Questo è rilevante solo (per quanto ne so) se si hanno più istruzioni T-SQL nel batch.

In altre parole, se la strategia di esecuzione ha ripresentazioni e desidera che vengano eseguite più istruzioni all'interno di una transazione, devono essere tutte in un unico batch come di seguito.

Per fare in modo che una transazione si estenda su più lotti, deve essere creata con db.Database.BeginTransaction. È esplicito questo BeginTransaction che Spiegazione the document you linked non è consentita in combinazione con tentativi. La transazione creata da TransactionalBehavior.EnsureTransaction è consentita indipendentemente dal criterio di riprova (perché è completamente gestito da EF).

// INSERT is rolled back due to error 
context.Database.ExecuteSqlCommand(
    TransactionalBehavior.EnsureTransaction, 
    @"INSERT INTO MyTable (i) VALUES (1) 
    RAISERROR('This exception was intentionally thrown', 16, 1)"); 

// INSERT is committed 
context.Database.ExecuteSqlCommand(
    TransactionalBehavior.DoNotEnsureTransaction, 
    @"INSERT INTO MyTable (i) VALUES (1) 
    RAISERROR('This exception was intentionally thrown', 16, 1)"); 

Il programma di test è di seguito.

private static void Main(string[] args) 
    { 
     //c:>sqlcmd -E 
     //1> create database EFTransaction 
     //2> go 
     //1> use EFTransaction 
     //2> go 
     //Changed database context to 'EFTransaction'. 
     //1> create table MyTable (i int primary key) 
     //2> go 
     const string connectionString = "Server=(local);Database=EFTransaction;Integrated Security=SSPI"; 

     using (DbContext context = new DbContext(connectionString)) 
     { 
      context.Database.ExecuteSqlCommand(
       TransactionalBehavior.DoNotEnsureTransaction, 
       @"IF EXISTS (SELECT * FROM sys.tables where name = 'MyTable') 
        DROP TABLE [dbo].[MyTable] 
       CREATE TABLE MyTable (i INT PRIMARY KEY)"); 
     } 

     Console.WriteLine("Insert one row."); 
     using (DbContext context = new DbContext(connectionString)) 
     { 
      context.Database.ExecuteSqlCommand(
       TransactionalBehavior.EnsureTransaction, 
       @"INSERT INTO MyTable (i) VALUES (0)"); 
      // Notice that there is no explicit COMMIT command required. 
     } 

     // Sanity check in a different connection that the row really was committed 
     using (DbContext context = new DbContext(connectionString)) 
     { 
      int rows = context.Database.SqlQuery<int>(
       "SELECT COUNT(*) FROM MyTable").Single(); 
      Console.WriteLine("Rows: {0}", rows); // Rows: 1 
     } 

     Console.WriteLine(); 
     Console.WriteLine("Insert one row and then throw an error, all within a transaction."); 
     Console.WriteLine("The error should cause the insert to be rolled back, so there should be no new rows"); 
     using (DbContext context = new DbContext(connectionString)) 
     { 
      try 
      { 
       context.Database.ExecuteSqlCommand(
        TransactionalBehavior.EnsureTransaction, 
        @"INSERT INTO MyTable (i) VALUES (1) 
        RAISERROR('This exception was intentionally thrown', 16, 1)"); 
      } 
      catch (SqlException e) 
      { 
       Console.WriteLine(e.Message); 
      } 

      int rows = context.Database.SqlQuery<int>(
       "SELECT COUNT(*) FROM MyTable").Single(); 
      Console.WriteLine("Rows: {0}", rows); // Rows: 1 
     } 

     Console.WriteLine(); 
     Console.WriteLine("Insert one row and then throw an error, all within a transaction."); 
     Console.WriteLine("The error will not cause the insert to be rolled back, so there should be 1 new row"); 
     using (DbContext context = new DbContext(connectionString)) 
     { 
      try 
      { 
       context.Database.ExecuteSqlCommand(
        TransactionalBehavior.DoNotEnsureTransaction, 
        @"INSERT INTO MyTable (i) VALUES (1) 
        RAISERROR('This exception was intentionally thrown', 16, 1)"); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.Message); 
      } 

      int rows = context.Database.SqlQuery<int>(
       "SELECT COUNT(*) FROM MyTable").Single(); 
      Console.WriteLine("Rows: {0}", rows); // Rows: 2 
     } 
    }