İşlemleri KullanmaUsing Transactions

İşlemler, çeşitli veritabanı işlemlerinin atomik bir şekilde işlenmesine izin verir.Transactions allow several database operations to be processed in an atomic manner. Hareket işlenirse, tüm işlemler veritabanına başarıyla uygulanır.If the transaction is committed, all of the operations are successfully applied to the database. Hareket geri alınırsa, işlemlerin hiçbiri veritabanına uygulanmaz.If the transaction is rolled back, none of the operations are applied to the database.

İpucu

Bu makalenin örneğini GitHub'da görüntüleyebilirsiniz.You can view this article's sample on GitHub.

Varsayılan hareket davranışıDefault transaction behavior

Varsayılan olarak, veritabanı sağlayıcısı hareketleri destekliyorsa, tek SaveChanges() bir çağrıdaki tüm değişiklikler bir harekette uygulanır.By default, if the database provider supports transactions, all changes in a single call to SaveChanges() are applied in a transaction. Değişikliklerden herhangi biri başarısız olursa, hareket geri alınır ve değişikliklerin hiçbiri veritabanına uygulanmaz.If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. Bu, SaveChanges() bir hata oluşursa veritabanının tamamen başarılı olması veya veritabanını değiştirilmeden bırakması garanti edilir.This means that SaveChanges() is guaranteed to either completely succeed, or leave the database unmodified if an error occurs.

Çoğu uygulama için bu varsayılan davranış yeterlidir.For most applications, this default behavior is sufficient. Yalnızca başvuru gereksinimleriniz gerekli yse hareketleri el ile denetlemeniz gerekir.You should only manually control transactions if your application requirements deem it necessary.

Hareketleri denetlemeControlling transactions

DbContext.Database Hareketleri başlatmak, işlemek ve geri almak için API'yi kullanabilirsiniz.You can use the DbContext.Database API to begin, commit, and rollback transactions. Aşağıdaki örnekte, SaveChanges() iki işlem ve tek bir işlemde yürütülmekte olan bir LINQ sorgusu gösterilmektedir.The following example shows two SaveChanges() operations and a LINQ query being executed in a single transaction.

Tüm veritabanı sağlayıcıları hareketleri desteklemez.Not all database providers support transactions. Bazı sağlayıcılar, işlem API'leri çağrıldığında atabilir veya işlem olmayabilir.Some providers may throw or no-op when transaction APIs are called.

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
        }
    }
}

Bağlamlar arası işlem (yalnızca ilişkisel veritabanları)Cross-context transaction (relational databases only)

Bir hareketi birden çok bağlam örnekleri arasında da paylaşabilirsiniz.You can also share a transaction across multiple context instances. Bu işlevsellik yalnızca ilişkisel veritabanlarının DbTransaction kullanımını ve DbConnectionilişkisel veritabanlarına özgü olmasını gerektirdiğinden ilişkisel bir veritabanı sağlayıcısı kullanırken kullanılabilir.This functionality is only available when using a relational database provider because it requires the use of DbTransaction and DbConnection, which are specific to relational databases.

Bir hareketi paylaşmak için bağlamların hem DbConnection a DbTransactionhem de bir' yi paylaşması gerekir.To share a transaction, the contexts must share both a DbConnection and a DbTransaction.

Bağlantının dışarıdan sağlanmasına izin verAllow connection to be externally provided

Bir DbConnection bağlantıyı oluşturmak için bir bağlama geçiş yeteneği gerektirir.Sharing a DbConnection requires the ability to pass a connection into a context when constructing it.

Dışarıdan sağlanmanın DbConnection en kolay yolu, bağlamı DbContext.OnConfiguring yapılandırmak ve bunları dışsal olarak DbContextOptions oluşturmak ve bağlam oluşturucuya aktarmak için yöntemi kullanmayı durdurmaktır.The easiest way to allow DbConnection to be externally provided, is to stop using the DbContext.OnConfiguring method to configure the context and externally create DbContextOptions and pass them to the context constructor.

İpucu

DbContextOptionsBuilderbağlamı DbContext.OnConfiguring yapılandırmak için kullandığınız API'dir, şimdi oluşturmak DbContextOptionsiçin harici olarak kullanacaksınız.DbContextOptionsBuilder is the API you used in DbContext.OnConfiguring to configure the context, you are now going to use it externally to create DbContextOptions.

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    { }

    public DbSet<Blog> Blogs { get; set; }
}

Alternatif olarak kullanmaya DbContext.OnConfiguringdevam etmek, DbConnection ancak kaydedilmiş ve DbContext.OnConfiguringdaha sonra kullanılan bir kabul etmektir.An alternative is to keep using DbContext.OnConfiguring, but accept a DbConnection that is saved and then used in DbContext.OnConfiguring.

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ı ve hareketi paylaşmaShare connection and transaction

Artık aynı bağlantıyı paylaşan birden çok bağlam örneği oluşturabilirsiniz.You can now create multiple context instances that share the same connection. DbContext.Database.UseTransaction(DbTransaction) Ardından, her iki bağlamı da aynı işlemde kullanmak için API'yi kullanın.Then use the DbContext.Database.UseTransaction(DbTransaction) API to enlist both contexts in the same transaction.

var options = new DbContextOptionsBuilder<BloggingContext>()
    .UseSqlServer(new SqlConnection(connectionString))
    .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();
            }

            // Commit transaction if all commands succeed, transaction will auto-rollback
            // when disposed if either commands fails
            transaction.Commit();
        }
        catch (Exception)
        {
            // TODO: Handle failure
        }
    }
}

Harici DbTransactions kullanma (yalnızca ilişkisel veritabanları)Using external DbTransactions (relational databases only)

İlişkisel bir 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.If you are using multiple data access technologies to access a relational database, you may want to share a transaction between operations performed by these different technologies.

Aşağıdaki örnek, aynı işlemde bir sqlclient işlemi ve bir Entity Framework Core işlemi ADO.NET nasıl gerçekleştirilini gösterir.The following example, shows how to perform an ADO.NET SqlClient operation and an Entity Framework Core operation in the same transaction.

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 (System.Exception)
        {
            // TODO: Handle failure
        }
    }
}

System.Transactions'ı KullanmaUsing System.Transactions

Not

Bu özellik EF Core 2.1'de yenidir.This feature is new in EF Core 2.1.

Daha geniş bir kapsamda koordine olmanız gerekiyorsa ortam hareketlerini kullanmak mümkündür.It is possible to use ambient transactions if you need to coordinate across a larger scope.

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 (System.Exception)
        {
            // TODO: Handle failure
        }
    }
}

Açık bir işlem için kaydolmak da mümkündür.It is also possible to enlist in an explicit transaction.

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 (System.Exception)
    {
        // TODO: Handle failure
    }
}

System.Transactions SınırlamalarıLimitations of System.Transactions

  1. EF Core, System.Transactions desteğini uygulamak için veritabanı sağlayıcılarına güvenir.EF Core relies on database providers to implement support for System.Transactions. Destek .NET Framework için ADO.NET sağlayıcılar arasında oldukça yaygın olmasına rağmen, API sadece son zamanlarda .NET Core eklenmiştir ve bu nedenle destek kadar yaygın değildir.Although support is quite common among ADO.NET providers for .NET Framework, the API has only been recently added to .NET Core and hence support is not as widespread. Bir sağlayıcı System.Transactions için destek uygulamazsa, bu API'lere yapılan çağrıların tamamen yoksayılması mümkündür.If a provider does not implement support for System.Transactions, it is possible that calls to these APIs will be completely ignored. .NET Core için SqlClient 2.1'den itibaren destekler.SqlClient for .NET Core does support it from 2.1 onwards. .NET Core 2.0 için SqlClient özelliğini kullanmaya çalışırsanız bir özel durum oluşturur.SqlClient for .NET Core 2.0 will throw an exception if you attempt to use the feature.

    Önemli

    Hareketleri yönetmek için güvenmeden önce API'nin sağlayıcınıza doğru şekilde hareket ettiğini test etmek önerilir.It is recommended that you test that the API behaves correctly with your provider before you rely on it for managing transactions. Yoksa veritabanı sağlayıcısının bakıcısına başvurmanız tavsiye edilir.You are encouraged to contact the maintainer of the database provider if it does not.

  2. Sürüm 2.1 itibariyle, .NET Core'daki System.Transactions uygulaması dağıtılmış hareketler için TransactionScope CommittableTransaction destek içermez, bu nedenle birden çok kaynak yöneticisi arasında hareketleri kullanamaz veya koordine edemezsiniz.As of version 2.1, the System.Transactions implementation in .NET Core does not include support for distributed transactions, therefore you cannot use TransactionScope or CommittableTransaction to coordinate transactions across multiple resource managers.