Entity Framework Core предоставляет гибкие механизмы для управления транзакциями, обеспечивая атомарность и согласованность данных при выполнении нескольких операций.
Поведение транзакции по умолчанию
Если поставщик базы данных поддерживает транзакции, то при вызове SaveChanges
все изменения применяются в одной транзакции. В случае ошибки все изменения откатываются, предотвращая частичное сохранение данных.
using var context = new AppDbContext();
var product = new Product { Name = "Laptop", Price = 1200 };
context.Products.Add(product);
context.SaveChanges();
Явное управление транзакциями
Для явного управления транзакциями можно использовать методы BeginTransaction
, Commit
и Rollback
.
using var context = new AppDbContext();
using var transaction = context.Database.BeginTransaction();
try
{
var product = new Product { Name = "Tablet", Price = 800 };
context.Products.Add(product);
context.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
Выполнение SQL-запросов в транзакции
Можно выполнять SQL-запросы внутри транзакции с помощью ExecuteSqlRaw
.
using var context = new AppDbContext();
using var transaction = context.Database.BeginTransaction();
try
{
context.Database.ExecuteSqlRaw("INSERT INTO Products (Name, Price) VALUES ('Smartphone', 500)");
context.Database.ExecuteSqlRaw("UPDATE Products SET Price = 550 WHERE Name = 'Smartphone'");
transaction.Commit();
}
catch
{
transaction.Rollback();
}
Использование точек сохранения
Entity Framework Core автоматически создает точку сохранения перед вызовом SaveChanges
. Можно также управлять точками сохранения вручную.
using var context = new AppDbContext();
using var transaction = context.Database.BeginTransaction();
try
{
context.Products.Add(new Product { Name = "Mouse", Price = 50 });
context.SaveChanges();
transaction.CreateSavepoint("BeforeMoreProducts");
context.Products.Add(new Product { Name = "Keyboard", Price = 100 });
context.Products.Add(new Product { Name = "Headphones", Price = 150 });
context.SaveChanges();
transaction.Commit();
}
catch
{
transaction.RollbackToSavepoint("BeforeMoreProducts");
}
Использование транзакции в нескольких контекстах
Для использования одной транзакции в нескольких экземплярах DbContext
нужно передавать общее подключение и транзакцию.
using var connection = new SqlConnection(connectionString);
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(connection)
.Options;
using var context1 = new AppDbContext(options);
using var transaction = await context1.Database.BeginTransactionAsync();
try
{
context1.Products.Add(new Product { Name = "Camera", Price = 1200 });
await context1.SaveChangesAsync();
using (var context2 = new AppDbContext(options))
{
await context2.Database.UseTransactionAsync(transaction.GetDbTransaction());
var products = await context2.Products.ToListAsync();
context2.Products.Add(new Product { Name = "Tripod", Price = 200 });
await context2.SaveChangesAsync();
}
await transaction.CommitAsync();
}
catch
{
transaction.Rollback();
}
Использование TransactionScope
TransactionScope
позволяет автоматически управлять транзакцией без явного вызова Commit
или Rollback
.
using System.Transactions;
using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
using var context = new AppDbContext();
var product = new Product { Name = "Monitor", Price = 300 };
context.Products.Add(product);
context.SaveChanges();
scope.Complete();
Использование внешних транзакций базы данных
Entity Framework Core поддерживает внешние транзакции, например, при комбинировании ADO.NET и EF Core.
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
using var transaction = (SqlTransaction)await connection.BeginTransactionAsync();
try
{
var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "DELETE FROM Products";
command.ExecuteNonQuery();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(connection)
.Options;
using (var context = new AppDbContext(options))
{
await context.Database.UseTransactionAsync(transaction);
context.Products.Add(new Product { Name = "Gaming Console", Price = 500 });
await context.SaveChangesAsync();
}
await transaction.CommitAsync();
}
catch
{
transaction.Rollback();
}