Azure中的ExecuteSqlCommand事务

时间:2015-07-02 13:55:28

标签: c# entity-framework azure azure-sql-database

我正在使用带有Azure Sql数据库的EF 6。据微软称,不支持用户发起的交易(参考:https://msdn.microsoft.com/en-us/data/dn307226#transactions

现在,使用EF 6,ExecuteSqlCommand默认包含在事务中:

  

从EF6 Database.ExecuteSqlCommand()开始,默认情况下会将命令包装在事务中(如果尚未存在)。 (参考:https://msdn.microsoft.com/en-us/data/dn456843.aspx

根据我的情况,我是否应该总是抑制ExecuteSqlCommand这样的交易行为:

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

2 个答案:

答案 0 :(得分:6)

您所指的此声明仅适用于重试策略:

  

配置执行策略后会导致重试......

您链接的文章并非特定于Azure。 Azure SQL数据库支持事务。

答案 1 :(得分:3)

是否要使用TransactionalBehavior.DoNotEnsureTransaction取决于您是否希望在命令范围内进行事务处理。如果批处理中有多个T-SQL语句,这只是相关的(据我所知)。

换句话说,如果您的执行策略已重试并希望在事务中执行多个语句,则它们必须全部在一个批处理中,如下所示。

为了使事务跨越多个批次,必须使用db.Database.BeginTransaction创建。明确的BeginTransactionthe 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
        }
    }