İş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 DbConnectionkullanı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ı DbTransactiongerekir.

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 DbContextOptionsiç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.OnConfiguringdevam etmek, ancak içinde kaydedilen ve kullanılan bir DbConnection değeri kabul etmek de DbContext.OnConfiguringkullanı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ı

  1. 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.

  2. 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.

  3. 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.