İşlemleri Kullanma
İş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.
Bahşiş
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();
using var transaction = context.Database.BeginTransaction();
try
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
context.SaveChanges();
var blogs = context.Blogs
.OrderBy(b => b.Url)
.ToList();
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
tüm ilişkisel veritabanı sağlayıcıları işlemleri desteklese de, işlem API'leri çağrıldığında diğer sağlayıcı türleri atabilir veya işlem yapılmayabilir.
Dekont
İş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ğlan Dayanıklılık.
Savepoints
Çağrıldığında SaveChanges
ve bağlam üzerinde bir işlem zaten devam ederken, EF herhangi bir veri kaydetmeden önce otomatik olarak bir kaydetme noktası oluşturur. Kayıt noktaları, bir hata oluşursa veya başka bir nedenle daha sonra geri alınabilecek 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 uyumsuz. MARS etkin olarak kullanımda olmasa bile bağlantıda MARS etkinleştirildiğinde ef tarafından kayıt noktaları oluşturulmaz. SaveChanges sırasında bir hata oluşursa, işlem bilinmeyen bir durumda bırakılabilir.
Aynı iş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();
using var transaction = context.Database.BeginTransaction();
try
{
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/dotnet/" });
context.SaveChanges();
transaction.CreateSavepoint("BeforeMoreBlogs");
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/visualstudio/" });
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/aspnet/" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception)
{
// If a failure occurred, we rollback to the savepoint and can continue the transaction
transaction.RollbackToSavepoint("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 yalnızca ilişkisel veritabanlarına özgü ve DbConnection
kullanımını gerektirdiğinden DbTransaction
ilişkisel veritabanı sağlayıcısı kullanılırken kullanılabilir.
Bir işlemi paylaşmak için bağlamların hem hem de ' DbConnection
yi paylaşması DbTransaction
gerekir.
Bağlantının dışarıdan sağlanmasına izin ver
paylaşımı, DbConnection
bir bağlantıyı oluştururken bağlama geçirebilmeyi gerektirir.
Dışarıdan sağlanmasına izin vermenin DbConnection
en kolay yolu, bağlamı DbContext.OnConfiguring
yapılandırmak için yöntemini kullanmayı durdurmak ve bunları dış olarak oluşturup DbContextOptions
bağlam oluşturucuya geçirmektir.
Bahşiş
DbContextOptionsBuilder
bağlamı yapılandırmak için kullandığınız DbContext.OnConfiguring
API'dir, şimdi oluşturmak DbContextOptions
için harici olarak kullanacaksınız.
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{
}
public DbSet<Blog> Blogs { get; set; }
}
Alternatif olarak kullanmaya DbContext.OnConfiguring
devam etmek, ancak içinde kaydedilen ve kullanılan bir DbConnection
değeri kabul etmek de DbContext.OnConfiguring
kullanılabilir.
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);
using var transaction = context1.Database.BeginTransaction();
try
{
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context1.SaveChanges();
using (var context2 = new BloggingContext(options))
{
context2.Database.UseTransaction(transaction.GetDbTransaction());
var blogs = context2.Blogs
.OrderBy(b => b.Url)
.ToList();
context2.Blogs.Add(new Blog { Url = "http://dot.net" });
context2.SaveChanges();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
Dış DbTransactions kullanma (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);
connection.Open();
using var transaction = connection.BeginTransaction();
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))
{
context.Database.UseTransaction(transaction);
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
System.Transactions kullanma
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);
connection.Open();
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
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))
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
}
// 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))
{
context.Database.OpenConnection();
context.Database.EnlistTransaction(transaction);
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();
// Run an EF Core command in the transaction
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
context.Database.CloseConnection();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
}
Dekont
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
TransactionScopeAsyncFlowOption.Enabled 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 işlemeyi/geri almayı desteklemez; bu, işlem tamamlanana kadar zaman uyumlu bir şekilde yürütme iş parçacığının engellenmesi anlamına gelir.