Aracılığıyla paylaş


İşlemlerle Çalışma

Dekont

Yalnızca EF6'ya Doğru - Bu sayfada ele alınan özellikler, API'ler vb. Entity Framework 6'da sunulmuştur. Önceki bir sürümü kullanıyorsanız, bilgilerin bir kısmı veya tümü geçerli değildir.

Bu belgede, işlemlerle çalışmayı kolaylaştırmak için EF5'ten bu yana eklediğimiz geliştirmeler de dahil olmak üzere EF6'daki işlemlerin kullanımı açıklanmaktadır.

EF'in varsayılan olarak yaptığı şey

Entity Framework'ün tüm sürümlerinde, veritabanına eklemek, güncelleştirmek veya silmek için SaveChanges() yürütürken çerçeve bu işlemi bir işlem içinde sarmalar. Bu işlem yalnızca işlemi yürütecek kadar uzun sürer ve ardından tamamlar. Başka bir işlem yürüttüğünde yeni bir işlem başlatılır.

VARSAYıLAN olarak EF6 Database.ExecuteSqlCommand() ile başlayarak, önceden mevcut olmayan bir işlemde komutu sarmalar. İsterseniz bu davranışı geçersiz kılmanıza olanak sağlayan bu yöntemin aşırı yüklemeleri vardır. Ayrıca, ObjectContext.ExecuteFunction() gibi API'ler aracılığıyla modele dahil edilen saklı yordamların EF6'da yürütülmesinde de aynı işlemi yapar (şu anda varsayılan davranışın geçersiz kılınamaması dışında).

Her iki durumda da işlemin yalıtım düzeyi, veritabanı sağlayıcısının varsayılan ayarını dikkate aldığı yalıtım düzeyidir. Varsayılan olarak, örneğin, SQL Server'da bu READ COMMITTED'dir.

Entity Framework bir işlemdeki sorguları sarmalamaz.

Bu varsayılan işlevsellik birçok kullanıcı için uygundur ve bu durumda EF6'da farklı bir şey yapmanıza gerek yoktur; her zaman yaptığınız gibi kodu yazın.

Ancak bazı kullanıcılar işlemleri üzerinde daha fazla denetime ihtiyaç duyar; bu, aşağıdaki bölümlerde ele alınmıştır.

API'ler nasıl çalışır?

EF6 Entity Framework'ün öncesinde veritabanı bağlantısının kendisini açmakta ısrar ediyordu (zaten açık olan bir bağlantı geçirildiyse bir özel durum oluşturdu). Bir işlem yalnızca açık bir bağlantıda başlatılabildiğinden, kullanıcının birkaç işlemi tek bir işleme sarmalamasının tek yolu transactionScope kullanmak veya ObjectContext.Bağlanion özelliğine gidin ve döndürülen Entity Bağlan ion nesnesi üzerinde Open() ve BeginTransaction() çağrısı yapmaya başlayın. Ayrıca, veritabanıyla iletişim kuran API çağrıları, temel alınan veritabanı bağlantısı üzerinde kendi başınıza bir işlem başlattıysanız başarısız olur.

Dekont

Entity Framework 6'da yalnızca kapalı bağlantıları kabul etme sınırlaması kaldırıldı. Ayrıntılar için bkz. Bağlan Ion Management.

EF6'dan başlayarak çerçeve artık aşağıdakileri sağlar:

  1. Database.BeginTransaction() : Kullanıcının mevcut bir DbContext içinde işlemleri başlatması ve tamamlaması için daha kolay bir yöntemdir. Bu yöntem, birden çok işlemin aynı işlem içinde birleştirilmesine olanak tanır ve bu nedenle tümü kaydedilmiş veya tümü tek bir işlem olarak geri alınır. Ayrıca kullanıcının işlem için yalıtım düzeyini daha kolay belirtmesini sağlar.
  2. Database.UseTransaction() : DbContext'in Entity Framework dışında başlatılan bir işlemi kullanmasına olanak tanır.

Birkaç işlemi aynı bağlamda tek bir işlemde birleştirme

Database.BeginTransaction() iki geçersiz kılmaya sahiptir: biri açık bir IsolationLevel alır, diğeri bağımsız değişken almaz ve temel veritabanı sağlayıcısından varsayılan IsolationLevel değerini kullanır. Her iki geçersiz kılma da, temel alınan depolama işlemi üzerinde işleme ve geri alma gerçekleştiren Commit() ve Rollback() yöntemleri sağlayan bir DbContextTransaction nesnesi döndürür.

DbContextTransaction, işlendikten veya geri alındıktan sonra atılması amaçlanmıştır. Bunu gerçekleştirmenin kolay yollarından biri, kullanım bloğu tamamlandığında Dispose() öğesini otomatik olarak çağıran using(...) {...} söz dizimidir:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
    class TransactionsExample
    {
        static void StartOwnTransactionWithinContext()
        {
            using (var context = new BloggingContext())
            {
                using (var dbContextTransaction = context.Database.BeginTransaction())
                {
                    context.Database.ExecuteSqlCommand(
                        @"UPDATE Blogs SET Rating = 5" +
                            " WHERE Name LIKE '%Entity Framework%'"
                        );

                    var query = context.Posts.Where(p => p.Blog.Rating >= 5);
                    foreach (var post in query)
                    {
                        post.Title += "[Cool Blog]";
                    }

                    context.SaveChanges();

                    dbContextTransaction.Commit();
                }
            }
        }
    }
}

Dekont

Bir işlemin başlaması için temel alınan depo bağlantısının açık olması gerekir. Bu nedenle Database.BeginTransaction() çağrısı henüz açılmamışsa bağlantıyı açar. DbContextTransaction bağlantıyı açtıysa Dispose() çağrıldığında kapatılır.

Var olan bir işlemi bağlama geçirme

Bazen kapsamı daha da geniş olan ve aynı veritabanındaki ancak EF'nin tamamen dışındaki işlemleri içeren bir işlem istersiniz. Bunu yapmak için bağlantıyı açıp işlemi kendiniz başlatmanız ve ardından EF a'ya önceden açılmış olan veritabanı bağlantısını kullanmasını ve b) bu bağlantıda var olan işlemi kullanmasını söylemeniz gerekir.

Bunu yapmak için bağlam sınıfınızda, i) mevcut bir bağlantı parametresini ve ii) contextOwns Bağlan ion boole değerini alan DbContext oluşturucularından birinden devralan bir oluşturucu tanımlamanız ve kullanmanız gerekir.

Dekont

Bu senaryoda çağrıldığında contextOwns Bağlan ion bayrağı false olarak ayarlanmalıdır. Bu, Entity Framework'e bağlantı tamamlandığında bağlantıyı kapatmaması gerektiğini bildirdiğinden önemlidir (örneğin, aşağıdaki 4. satıra bakın):

using (var conn = new SqlConnection("..."))
{
    conn.Open();
    using (var context = new BloggingContext(conn, contextOwnsConnection: false))
    {
    }
}

Ayrıca, işlemi kendiniz başlatmanız (varsayılan ayarı önlemek istiyorsanız IsolationLevel dahil) ve Entity Framework'e bağlantıda zaten başlatılan bir işlem olduğunu bildirmeniz gerekir (aşağıdaki 33. satıra bakın).

Ardından veritabanı işlemlerini doğrudan Sql Bağlan ion üzerinde veya DbContext üzerinde yürütebilirsiniz. Bu tür tüm işlemler tek bir işlem içinde yürütülür. İşlemin işlenmesi veya geri alınması ve üzerinde Dispose() çağrısının yanı sıra veritabanı bağlantısını kapatıp yok etme sorumluluğunu alırsınız. Örnek:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
     class TransactionsExample
     {
        static void UsingExternalTransaction()
        {
            using (var conn = new SqlConnection("..."))
            {
               conn.Open();

               using (var sqlTxn = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot))
               {
                   var sqlCommand = new SqlCommand();
                   sqlCommand.Connection = conn;
                   sqlCommand.Transaction = sqlTxn;
                   sqlCommand.CommandText =
                       @"UPDATE Blogs SET Rating = 5" +
                        " WHERE Name LIKE '%Entity Framework%'";
                   sqlCommand.ExecuteNonQuery();

                   using (var context =  
                     new BloggingContext(conn, contextOwnsConnection: false))
                    {
                        context.Database.UseTransaction(sqlTxn);

                        var query =  context.Posts.Where(p => p.Blog.Rating >= 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }
                       context.SaveChanges();
                    }

                    sqlTxn.Commit();
                }
            }
        }
    }
}

İşlemi temizleme

Entity Framework'ün geçerli işlemle ilgili bilgilerini temizlemek için Database.UseTransaction() öğesine null geçirebilirsiniz. Entity Framework, bunu yaptığınızda mevcut işlemi işlemez veya geri almaz. Bu nedenle, yalnızca yapmak istediğiniz şeyin bu olduğundan eminseniz dikkatli olun.

UseTransaction'daki hatalar

Aşağıdaki durumlarda bir işlem geçirirseniz Database.UseTransaction() özel durumu görürsünüz:

  • Entity Framework'ün zaten bir işlemi var
  • Entity Framework zaten bir TransactionScope içinde çalışıyor
  • Geçirilen işlemdeki bağlantı nesnesi null. Başka bir ifadeyle, işlem bir bağlantıyla ilişkilendirilmemiştir; bu genellikle işlemin zaten tamamlandığının işaretidir
  • Geçirilen işlemdeki bağlantı nesnesi Entity Framework'ün bağlantısıyla eşleşmiyor.

İşlemleri diğer özelliklerle kullanma

Bu bölümde, yukarıdaki işlemlerin nasıl etkileşimde olduğu ayrıntılı olarak anlatlanmıştır:

  • Bağlantı dayanıklılığı
  • Zaman uyumsuz yöntemler
  • TransactionScope işlemleri

Bağlantı Dayanıklılığı

Yeni Bağlan Ion Dayanıklılık özelliği, kullanıcı tarafından başlatılan işlemlerle çalışmaz. Ayrıntılar için bkz . Yürütme Stratejilerini Yeniden Deneme.

Zaman Uyumsuz Programlama

Önceki bölümlerde açıklanan yaklaşımda zaman uyumsuz sorguyla çalışmak ve yöntemleri kaydetmek için başka seçenek veya ayar gerekmez. Ancak, zaman uyumsuz yöntemler içinde ne yaptığınıza bağlı olarak, bunun uzun süre çalışan işlemlere neden olabileceğini unutmayın; bu da kilitlenmelere veya engellemeye neden olabilir ve bu da genel uygulamanın performansı için kötü olabilir.

TransactionScope İşlemleri

EF6'nın öncesinde, daha büyük kapsam işlemleri sağlamanın önerilen yolu bir TransactionScope nesnesi kullanmaktı:

using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
    class TransactionsExample
    {
        static void UsingTransactionScope()
        {
            using (var scope = new TransactionScope(TransactionScopeOption.Required))
            {
                using (var conn = new SqlConnection("..."))
                {
                    conn.Open();

                    var sqlCommand = new SqlCommand();
                    sqlCommand.Connection = conn;
                    sqlCommand.CommandText =
                        @"UPDATE Blogs SET Rating = 5" +
                            " WHERE Name LIKE '%Entity Framework%'";
                    sqlCommand.ExecuteNonQuery();

                    using (var context =
                        new BloggingContext(conn, contextOwnsConnection: false))
                    {
                        var query = context.Posts.Where(p => p.Blog.Rating > 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }
                        context.SaveChanges();
                    }
                }

                scope.Complete();
            }
        }
    }
}

Sql Bağlan ion ve Entity Framework hem ortam TransactionScope işlemini kullanır hem de birlikte işlenir.

.NET 4.5.1 ile başlayarak TransactionScope, TransactionScopeAsyncFlowOption sabit listesi kullanılarak zaman uyumsuz yöntemlerle de çalışacak şekilde güncelleştirildi:

using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
    class TransactionsExample
    {
        public static void AsyncTransactionScope()
        {
            using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                using (var conn = new SqlConnection("..."))
                {
                    await conn.OpenAsync();

                    var sqlCommand = new SqlCommand();
                    sqlCommand.Connection = conn;
                    sqlCommand.CommandText =
                        @"UPDATE Blogs SET Rating = 5" +
                            " WHERE Name LIKE '%Entity Framework%'";
                    await sqlCommand.ExecuteNonQueryAsync();

                    using (var context = new BloggingContext(conn, contextOwnsConnection: false))
                    {
                        var query = context.Posts.Where(p => p.Blog.Rating > 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }

                        await context.SaveChangesAsync();
                    }
                }
                
                scope.Complete();
            }
        }
    }
}

TransactionScope yaklaşımında hala bazı sınırlamalar vardır:

  • Zaman uyumsuz yöntemlerle çalışmak için .NET 4.5.1 veya üzeri gerekir.
  • Tek bir bağlantınız olduğundan emin değilseniz (bulut senaryoları dağıtılmış işlemleri desteklemez) bulut senaryolarında kullanılamaz.
  • Önceki bölümlerin Database.UseTransaction() yaklaşımıyla birleştirilemez.
  • Herhangi bir DDL yayınlarsanız ve MSDTC Hizmeti aracılığıyla dağıtılmış işlemleri etkinleştirmediyseniz özel durumlar oluşturur.

TransactionScope yaklaşımının avantajları:

  • Belirli bir veritabanına birden fazla bağlantı kurarsanız veya aynı işlem içinde farklı bir veritabanına bağlantı olan bir veritabanı bağlantısını birleştirirseniz yerel bir işlemi otomatik olarak dağıtılmış işleme yükseltecektir (not: Bunun için dağıtılmış işlemlere izin verecek şekilde YAPıLANDıRıLMıŞ BIR MSDTC hizmetiniz olmalıdır).
  • Kodlama kolaylığı. İşlemin ortam olmasını ve açıkça denetiminiz altında olmak yerine arka planda örtük olarak ele alınması tercih ederseniz TransactionScope yaklaşımı size daha uygun olabilir.

Özetle, yukarıdaki yeni Database.BeginTransaction() ve Database.UseTransaction() API'leriyle, çoğu kullanıcı için TransactionScope yaklaşımı artık gerekli değildir. TransactionScope kullanmaya devam ederseniz yukarıdaki sınırlamalara dikkat edin. Mümkün olduğunda bunun yerine önceki bölümlerde özetlenen yaklaşımı kullanmanızı öneririz.