Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
İşlemler, birkaç veritabanı işleminin atomik bir şekilde işlenmesini sağlar. İşlem işlenirse, tüm işlemler veritabanına başarıyla uygulanır. İşlem geri alınırsa, işlemlerin hiçbiri veritabanına uygulanmaz.
Tavsiye
Bu makalenin örneğini GitHub'da görüntüleyebilirsiniz.
Varsayılan işlem davranışı
Varsayılan olarak, veritabanı sağlayıcısı işlemleri destekliyorsa, tek bir çağrıdaki SaveChanges
tüm değişiklikler bir işleme uygulanır. Değişikliklerden herhangi biri başarısız olursa işlem geri alınır ve değişikliklerin hiçbiri veritabanına uygulanmaz. Bu, tamamen başarılı olması veya hata oluşması durumunda veritabanını değiştirmeden bırakmanın garanti olduğu anlamına gelir SaveChanges
.
Çoğu uygulama için bu varsayılan davranış yeterlidir. İşlemleri yalnızca uygulama gereksinimleriniz gerekli görürse el ile denetlemeniz gerekir.
İşlemleri denetleme
API'yi DbContext.Database
kullanarak işlemleri başlatabilir, işleyebilir ve geri alabilirsiniz. Aşağıdaki örnekte iki SaveChanges
işlem ve tek bir işlemde yürütülen bir LINQ sorgusu gösterilmektedir:
using var context = new BloggingContext();
await using var transaction = await context.Database.BeginTransactionAsync();
try
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context.SaveChangesAsync();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
await context.SaveChangesAsync();
var blogs = await context.Blogs
.OrderBy(b => b.Url)
.ToListAsync();
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
await transaction.CommitAsync();
}
catch (Exception)
{
// TODO: Handle failure
}
Tüm ilişkisel veritabanı sağlayıcıları işlemleri desteklese de, başka sağlayıcı türleri işlem API'leri çağrıldığında hata verebilir veya no-op olabilir.
Uyarı
İşlemleri bu şekilde el ile denetlemek, örtük olarak çağrılan yeniden deneme yürütme stratejileriyle uyumlu değildir. Daha fazla bilgi için bkz. Bağlantı Dayanıklılığı .
Kayıt noktaları
Çağrıldığında SaveChanges
ve bağlamda bir işlem zaten devam ediyorsa, EF veri kaydedilmeden önce otomatik olarak bir kaydetme noktası oluşturur. Kaydetme noktaları, bir hata oluşursa veya başka bir nedenle daha sonra geri dönülebilecek bir veritabanı işlemi içindeki noktalardır. Herhangi bir hatayla karşılaşırsa SaveChanges
, işlemi otomatik olarak kayıt noktasına geri alır ve işlemi hiç başlatılmamış gibi aynı durumda bırakır. Bu, özellikle iyimser eşzamanlılık sorunları oluştuğunda sorunları düzeltmenize ve kaydetmeyi yeniden denemenize olanak tanır.
Uyarı
Kayıt noktaları, SQL Server'ın Birden Çok Etkin Sonuç Kümesi (MARS) ile uyumlu değildir. MARS etkin olmasa bile bağlantıda MARS etkinleştirildiğinde EF tarafından kayıt noktaları oluşturulmayacaktır. SaveChanges sırasında bir hata oluşursa, işlem bilinmeyen bir durumda bırakılabilir.
İşlemlerde olduğu gibi, kayıt noktalarını el ile yönetmek de mümkündür. Aşağıdaki örnek, bir işlem içinde bir kayıt noktası oluşturur ve hata durumunda bu kayıt noktasına geri döner:
using var context = new BloggingContext();
await using var transaction = await context.Database.BeginTransactionAsync();
try
{
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/dotnet/" });
await context.SaveChangesAsync();
await transaction.CreateSavepointAsync("BeforeMoreBlogs");
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/visualstudio/" });
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/aspnet/" });
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch (Exception)
{
// If a failure occurred, we rollback to the savepoint and can continue the transaction
await transaction.RollbackToSavepointAsync("BeforeMoreBlogs");
// TODO: Handle failure, possibly retry inserting blogs
}
Bağlamlar arası işlem
Bir işlemi birden çok bağlam örneğinde de paylaşabilirsiniz. Bu işlevsellik, DbTransaction
ve DbConnection
kullanımını gerektirdiğinden ve bunlar ilişkisel veritabanlarına özgü olduğundan, yalnızca ilişkisel veritabanı sağlayıcısı kullanılırken kullanılabilir.
Bir işlemi paylaşmak için bağlamların hem DbConnection
hem de DbTransaction
paylaşması gerekir.
Bağlantının dışarıdan sağlanmasına izin ver
Bir DbConnection
paylaşımı, bir bağlantıyı oluştururken bağlama koyabilme yeteneğini gerektirir.
Dışarıdan DbConnection
sağlanmasına izin vermenin en kolay yolu, bağlamı DbContext.OnConfiguring
yöntemiyle yapılandırmayı durdurmak ve DbContextOptions
öğelerini dışarıdan oluşturup, bağlam oluşturucusuna geçirmektir.
Tavsiye
DbContextOptionsBuilder
bağlamı yapılandırmak için DbContext.OnConfiguring
'de kullandığınız API'dir; şimdi DbContextOptions
oluşturmak için onu dışarıda kullanacaksınız.
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{
}
public DbSet<Blog> Blogs { get; set; }
}
Alternatif olarak, DbContext.OnConfiguring
kullanmaya devam etmek ve ardından DbConnection
içinde kullanılan kaydedilmiş bir DbContext.OnConfiguring
'yi kabul etmektir.
public class BloggingContext : DbContext
{
private DbConnection _connection;
public BloggingContext(DbConnection connection)
{
_connection = connection;
}
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connection);
}
}
Bağlantıyı ve işlemi paylaşma
Artık aynı bağlantıyı paylaşan birden çok bağlam örneği oluşturabilirsiniz. Ardından API'yi DbContext.Database.UseTransaction(DbTransaction)
kullanarak her iki bağlamı da aynı işlemde listeleyin.
using var connection = new SqlConnection(connectionString);
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
using var context1 = new BloggingContext(options);
await using var transaction = await context1.Database.BeginTransactionAsync();
try
{
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context1.SaveChangesAsync();
using (var context2 = new BloggingContext(options))
{
await context2.Database.UseTransactionAsync(transaction.GetDbTransaction());
var blogs = await context2.Blogs
.OrderBy(b => b.Url)
.ToListAsync();
context2.Blogs.Add(new Blog { Url = "http://dot.net" });
await context2.SaveChangesAsync();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
await transaction.CommitAsync();
}
catch (Exception)
{
// TODO: Handle failure
}
Harici DbTransactions kullanımı (yalnızca ilişkisel veritabanları)
İlişkisel veritabanına erişmek için birden çok veri erişim teknolojisi kullanıyorsanız, bu farklı teknolojiler tarafından gerçekleştirilen işlemler arasında bir işlem paylaşmak isteyebilirsiniz.
Aşağıdaki örnekte, aynı işlemde ADO.NET SqlClient işleminin ve Entity Framework Core işleminin nasıl gerçekleştirleneceği gösterilmektedir.
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
await using var transaction = (SqlTransaction)await connection.BeginTransactionAsync();
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();
// Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
using (var context = new BloggingContext(options))
{
await context.Database.UseTransactionAsync(transaction);
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context.SaveChangesAsync();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
await transaction.CommitAsync();
}
catch (Exception)
{
// TODO: Handle failure
}
System.Transactions kullanmak
Daha büyük bir kapsam genelinde koordine etmeniz gerekiyorsa ortam işlemlerini kullanabilirsiniz.
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
await command.ExecuteNonQueryAsync();
// Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
using (var context = new BloggingContext(options))
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context.SaveChangesAsync();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
scope.Complete();
}
catch (Exception)
{
// TODO: Handle failure
}
}
Ayrıca, açık bir işleme de kaydolabilir.
using (var transaction = new CommittableTransaction(
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
var connection = new SqlConnection(connectionString);
try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
using (var context = new BloggingContext(options))
{
await context.Database.OpenConnectionAsync();
context.Database.EnlistTransaction(transaction);
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
await command.ExecuteNonQueryAsync();
// Run an EF Core command in the transaction
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context.SaveChangesAsync();
await context.Database.CloseConnectionAsync();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
}
Uyarı
Zaman uyumsuz API'ler kullanıyorsanız, ortam işleminin zaman uyumsuz çağrılar arasında akmasını sağlamak için oluşturucuda TransactionScope
değerini belirttiğinizden emin olun.
ve ortam işlemleri hakkında TransactionScope
daha fazla bilgi için bu belgelere bakın.
System.Transactions Sınırlamaları
EF Core, System.Transactions desteğini uygulamak için veritabanı sağlayıcılarına dayanır. Bir sağlayıcı System.Transactions için destek uygulamazsa, bu API'lere yapılan çağrılar tamamen yoksayılabilir. SqlClient bunu destekler.
Önemli
İşlemleri yönetmek için api'ye güvenmeden önce API'nin sağlayıcınızla doğru şekilde davranıp davranmadığını test edin. Aksi takdirde veritabanı sağlayıcısının bakımcısına başvurmanız tavsiye edilir.
System.Transactions içindeki dağıtılmış işlem desteği yalnızca Windows için .NET 7.0'a eklendi. Dağıtılmış işlemleri eski .NET sürümlerinde veya Windows dışı platformlarda kullanma girişimleri başarısız olur.
TransactionScope zaman uyumsuz taahhüt/geri almayı desteklemez; bu da işlem tamamlanana kadar senkron biçimde yürütme iş parçacığını engellediği anlamına gelir.