Se utilizzare o meno TransactionalBehavior.DoNotEnsureTransaction
dipende dal fatto che si desidera che ci sia una transazione durante l'ambito del comando. Questo è rilevante solo (per quanto ne so) se si hanno più istruzioni T-SQL nel batch.
In altre parole, se la strategia di esecuzione ha ripresentazioni e desidera che vengano eseguite più istruzioni all'interno di una transazione, devono essere tutte in un unico batch come di seguito.
Per fare in modo che una transazione si estenda su più lotti, deve essere creata con db.Database.BeginTransaction
. È esplicito questo BeginTransaction
che Spiegazione the document you linked non è consentita in combinazione con tentativi. La transazione creata da TransactionalBehavior.EnsureTransaction
è consentita indipendentemente dal criterio di riprova (perché è completamente gestito da 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)");
Il programma di test è di seguito.
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
}
}
fonte
2015-07-06 19:01:38
Forse sto fraintendendo, ma non è consigliabile utilizzare una strategia di esecuzione di nuovo tentativo quando si lavora con un database SQL di Azure? In quanto tale, se utilizzo una strategia del genere, c'è qualche preoccupazione riguardo al comportamento predefinito di "ExecuteSqlCommand', o sono semplicemente paranoico e sto leggendo male le cose? –
Il tentativo deve essere eseguito a livello di transazione. Per questo motivo ritengo il supporto per i tentativi in EF piuttosto inutile. EF non può riprovare la transazione, può riprovare una singola query o la chiamata SaveChanges. È il livello sbagliato per riprovare. Se si lasciano le transazioni di controllo EF, verrà utilizzata una transazione per query/salvataggio. Le strategie di riprova lo supportano. Ma ti consiglio di usare le transazioni corrette e riprovare (magari usando un semplice metodo di supporto) .; EF può avvolgere un ciclo retry attorno al proprio codice. Ma non può avvolgere un ciclo di tentativi intorno al * codice *. Quindi ogni singola chiamata a EF può essere ripetuta, ma non è possibile effettuare più chiamate. – usr