C# Azure中ExecuteSqlCommand的事务
我正在使用EF6和Azure Sql数据库。根据Microsoft的说法,不支持用户发起的事务。参考: 现在,在EF 6中,默认情况下ExecuteSqlCommand包装在事务中: 默认情况下,从EF6 Database.EXECUTESQLCOMAND开始,如果事务中还没有命令,则会将该命令包装到事务中。参考: 考虑到我的场景,我是否应该始终抑制ExecuteSqlCommand事务性行为,如下所示:C# Azure中ExecuteSqlCommand的事务,c#,entity-framework,azure,azure-sql-database,C#,Entity Framework,Azure,Azure Sql Database,我正在使用EF6和Azure Sql数据库。根据Microsoft的说法,不支持用户发起的事务。参考: 现在,在EF 6中,默认情况下ExecuteSqlCommand包装在事务中: 默认情况下,从EF6 Database.EXECUTESQLCOMAND开始,如果事务中还没有命令,则会将该命令包装到事务中。参考: 考虑到我的场景,我是否应该始终抑制ExecuteSqlCommand事务性行为,如下所示: context.Database.ExecuteSqlCommand(Transactio
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, @"TRUNCATE TABLE Person;");
您所指的此语句仅适用于重试策略: 当您配置了导致重试的执行策略时
您链接到的文章不是特定于Azure的。Azure SQL数据库支持事务。是否要使用TransactionalBehavior.DoNotEnsureTransaction取决于您是否希望在命令范围内存在事务。这仅与我所知的批处理中有多个T-SQL语句有关 换句话说,如果您的执行策略有重试,并且希望在一个事务中执行多个语句,那么它们必须全部在一个批中,如下所示 为了使事务跨越多个批处理,必须使用db.Database.BeginTransaction创建事务。正是这种显式的BeginTransaction解释了不允许与重试结合使用。无论重试策略如何,都允许TransactionalBehavior.EnsureRetransaction创建的事务,因为它完全由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
}
}
也许我有误解,但在使用Azure SQL数据库时,是否不建议使用重试执行策略?因此,如果我确实使用了这样的策略,是否有人担心ExecuteSqlCommand的默认行为,或者我只是有点偏执和误读?重试必须在事务级别进行。出于这个原因,我认为EF的重试支持毫无意义。EF无法重试事务,它可以重试单个查询或SaveChanges调用。重试的级别不正确。如果让EF控制事务,则每次查询/保存将使用一个tran。重试策略支持这一点。但我建议您使用适当的事务,并使用简单的助手方法重试。;EF可以围绕自己的代码进行重试循环。但是它不能在代码周围包装一个重试循环。因此,可以重试对EF的任何单个调用,但不能重试多个调用。