Azure 中 ExecuteSqlCommand 的事务
Transaction for ExecuteSqlCommand in Azure
我将 EF 6 与 Azure Sql 数据库一起使用。根据 Microsoft 的说法,不支持用户发起的交易(参考:https://msdn.microsoft.com/en-us/data/dn307226#transactions)
现在,对于 EF 6,ExecuteSqlCommand
默认包装在事务中:
Starting with EF6 Database.ExecuteSqlCommand() by default will wrap the command in a transaction if one was not already present. (ref: https://msdn.microsoft.com/en-us/data/dn456843.aspx)
鉴于我的情况,我是否应该始终抑制这样的 ExecuteSqlCommand
事务行为:
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, @"TRUNCATE TABLE Person;");
您所指的此声明仅适用于重试策略:
When you have configured an execution strategy that results in retries ...
您链接到的文章并非特定于 Azure。 Azure SQL 数据库支持事务。
是否要使用TransactionalBehavior.DoNotEnsureTransaction
取决于您是否希望在命令范围内有事务。这仅在批处理中有多个 T-SQL 语句时才有意义(据我所知)。
换句话说,如果你的执行策略有重试并且想要在一个事务中执行多个语句,它们必须像下面这样全部在一个批次中。
为了使交易跨越多个批次,必须使用 db.Database.BeginTransaction
创建它。 the document you linked 解释的显式 BeginTransaction
不允许与重试结合使用。无论重试策略如何,都允许 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
}
}
我将 EF 6 与 Azure Sql 数据库一起使用。根据 Microsoft 的说法,不支持用户发起的交易(参考:https://msdn.microsoft.com/en-us/data/dn307226#transactions)
现在,对于 EF 6,ExecuteSqlCommand
默认包装在事务中:
Starting with EF6 Database.ExecuteSqlCommand() by default will wrap the command in a transaction if one was not already present. (ref: https://msdn.microsoft.com/en-us/data/dn456843.aspx)
鉴于我的情况,我是否应该始终抑制这样的 ExecuteSqlCommand
事务行为:
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, @"TRUNCATE TABLE Person;");
您所指的此声明仅适用于重试策略:
When you have configured an execution strategy that results in retries ...
您链接到的文章并非特定于 Azure。 Azure SQL 数据库支持事务。
是否要使用TransactionalBehavior.DoNotEnsureTransaction
取决于您是否希望在命令范围内有事务。这仅在批处理中有多个 T-SQL 语句时才有意义(据我所知)。
换句话说,如果你的执行策略有重试并且想要在一个事务中执行多个语句,它们必须像下面这样全部在一个批次中。
为了使交易跨越多个批次,必须使用 db.Database.BeginTransaction
创建它。 the document you linked 解释的显式 BeginTransaction
不允许与重试结合使用。无论重试策略如何,都允许 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
}
}