2015-07-02 3 views
8

Я использую EF 6 с базой данных Azure Sql. По данным Microsoft, инициированной пользователем транзакции не поддерживаются (реф: https://msdn.microsoft.com/en-us/data/dn307226#transactions)Транзакция для ExecuteSqlCommand в Azure

Теперь, с EF 6, ExecuteSqlCommand заворачивают в сделке по умолчанию:

Начиная с Ef6 Database.ExecuteSqlCommand (по умолчанию) завершает команду в транзакции, если ее еще нет. (Ссылка: https://msdn.microsoft.com/en-us/data/dn456843.aspx)

Учитывая мой сценарий, я должен всегда подавлять ExecuteSqlCommand транзакционной поведение, как это:

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

ответ

6

Это утверждение, что вы имеете в виду относится только повторить стратегии:

Когда вы настроили стратегию выполнения, которая приводит к повторным попыткам ...

Статья, к которой вы обращались, не относится к Azure. База данных Azure SQL поддерживает транзакции.

+0

Возможно, я недопонимаю, но не рекомендуется ли использовать стратегию выполнения повторных попыток при работе с Azure SQL Database? Как таковая, если я использую такую ​​стратегию, есть ли какая-либо озабоченность по поводу поведения ExecuteSqlCommand' по умолчанию, или я просто являюсь параноидальным и неправильно читающим? –

+0

Повторение должно происходить на уровне транзакции. По этой причине я считаю, что поддержка повторных попыток в EF совершенно бессмысленна. EF не может повторить транзакцию, она может повторить один запрос или вызов SaveChanges. Это неправильный уровень, на который нужно повторить попытку. Если вы позволяете управлять транзакциями EF, он будет использовать один tran для запроса/сохранения. Стратегии повтора поддерживают это. Но я советую вам использовать правильные транзакции и повторить попытку (возможно, используя простой вспомогательный метод); EF может обернуть цикл повтора вокруг своего собственного кода. Но он не может обернуть цикл повтора вокруг * вашего * кода. Таким образом, любой вызов EF может быть повторен, но множественные вызовы не могут. – usr

3

Независимо от того, хотите ли вы использовать TransactionalBehavior.DoNotEnsureTransaction, зависит от того, хотите ли вы, чтобы была транзакция во время действия команды. Это актуально (насколько я знаю), если в пакете имеется несколько операторов T-SQL.

Другими словами, если ваша стратегия выполнения имеет повторные попытки и требует, чтобы в транзакции выполнялось несколько операторов, они должны быть в одной партии, как показано ниже.

Для того чтобы транзакция охватывала несколько партий, ее необходимо было бы создать с помощью db.Database.BeginTransaction. Именно этот явный BeginTransaction объясняет, что the document you linked объясняет, что это не допускается в сочетании с попытками. Транзакция, созданная TransactionalBehavior.EnsureTransaction, разрешена независимо от политики повтора (поскольку она полностью управляется 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)"); 

Программа испытаний находится ниже.

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 
     } 
    } 
Смежные вопросы