2015-07-02 25 views
8

Używam EF 6 z bazą danych Azure Sql. Według Microsoftu, transakcje zainicjowane przez użytkownika nie są obsługiwane (Ref: https://msdn.microsoft.com/en-us/data/dn307226#transactions)Transakcja dla ExecuteSqlCommand na Azure

Teraz EF 6, ExecuteSqlCommand jest owinięty w transakcji domyślnie:

Począwszy EF6 Database.ExecuteSqlCommand (domyślnie) zawinie polecenie w transakcji, jeśli jeszcze nie było. (Ref: https://msdn.microsoft.com/en-us/data/dn456843.aspx)

Biorąc pod uwagę mój scenariusz, mam zawsze tłumią ExecuteSqlCommand transakcyjny zachowanie tak:

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

Odpowiedz

6

To stwierdzenie, które odnoszą się do odnosi się tylko do ponowić strategie:

Po skonfigurowaniu strategii wykonywania, która powoduje ponowne próby ...

Artykuł, z którym łączyłeś się, nie jest specyficzny dla Azure. Baza danych Azure SQL obsługuje transakcje.

+0

Być może nie rozumiem, ale czy nie zaleca się stosowania strategii wykonywania ponownej próby podczas pracy z bazą danych Azure SQL? W związku z tym, jeśli używam takiej strategii, czy istnieje jakiekolwiek obawy dotyczące domyślnego zachowania 'ExecuteSqlCommand', czy też po prostu jestem paranoikiem i nie rozumiem? –

+0

Ponowna próba musi nastąpić na poziomie transakcji. Z tego powodu uważam, że ponawiane wsparcie w EF jest zupełnie bezcelowe. EF nie może ponowić próby transakcji, może ponowić próbę pojedynczego zapytania lub wywołania SaveChanges. Nieprawidłowy poziom do ponowienia. Jeśli zezwolisz na transakcje kontrolne EF, użyje jednego transu na zapytanie/zapisz. Służą do tego strategie Retry. Ale radzę użyć odpowiednich transakcji i spróbować ponownie (może używając prostej metody pomocniczej) .; EF może zawinąć pętlę sparowania wokół własnego kodu. Ale nie może zawijać pętli sparowania wokół * twojego * kodu. Zatem każde pojedyncze wywołanie EF może zostać powtórzone, ale wiele połączeń nie może. – usr

3

To, czy chcesz używać TransactionalBehavior.DoNotEnsureTransaction, zależy od tego, czy chcesz mieć transakcję podczas wykonywania polecenia. Jest to istotne (o ile mi wiadomo), jeśli w partii jest wiele instrukcji T-SQL.

Innymi słowy, jeśli twoja strategia wykonawcza wymaga ponownej próby i chcesz, aby kilka transakcji zostało wykonanych w ramach transakcji, muszą one być w jednej partii, jak poniżej.

Aby transakcja obejmowała wiele partii, musiałaby zostać utworzona za pomocą db.Database.BeginTransaction. Wyjaśnienie tego nie jest dozwolone w połączeniu z próbami. Transakcja utworzona przez TransactionalBehavior.EnsureTransaction jest dozwolona bez względu na politykę ponawiania (ponieważ jest w pełni zarządzana przez 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)"); 

Program testowy znajduje się poniżej.

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 
     } 
    }