NOLOCK ile Varlık Çerçevesi


Yanıtlar:


207

Hayır, ancak bir işlem başlatabilir ve yalıtım düzeyini sırasız okunacak şekilde ayarlayabilirsiniz . Bu esasen NOLOCK ile aynıdır, ancak bunu tablo başına yapmak yerine, işlem kapsamındaki her şey için yapacaktır.

Bu istediğiniz gibi görünüyorsa, bunu nasıl yapabileceğiniz ...

//declare the transaction options
var transactionOptions = new System.Transactions.TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new System.Transactions.TransactionScope(
    System.Transactions.TransactionScopeOption.Required, 
    transactionOptions)
)

//declare our context
using (var context = new MyEntityConnection())
{
    //any reads we do here will also read uncomitted data
    //...
    //...
    //don't forget to complete the transaction scope
    transactionScope.Complete();
}

Excellent @DoctaJonez EF4'te bunun için yeni bir şey getirildi mi?
FMFF

@FMFF EF4 için yeni bir şey getirilip getirilmediğini bilmiyorum. Yukarıdaki kodun EFv1 ve üstü ile çalıştığını biliyorum.
Doktor Jones

sonuç ne olurdu? birisi yukarıda belirtilen blokta transactionScope.Complete () 'ı atlarsa? Bunun için başka bir soru sormam gerektiğini mi düşünüyorsun?
Eakan Gopalakrishnan

@EakanGopalakrishnan İşlem yöneticisi bunu bir sistem hatası veya işlem kapsamında atılan istisnalar olarak değerlendirdiğinden, bu yöntem çağrılmazsa işlem iptal edilir. (MSDN msdn.microsoft.com/en-us/library/… adresinden alınmıştır )
Doctor Jones

1
@JsonStatham , kilometre taşı 2.1.0 için olan bu çekme talebine eklendi
Doctor Jones

83

Uzantı yöntemleri bunu kolaylaştırabilir

public static List<T> ToListReadUncommitted<T>(this IQueryable<T> query)
{
    using (var scope = new TransactionScope(
        TransactionScopeOption.Required, 
        new TransactionOptions() { 
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
    {
        List<T> toReturn = query.ToList();
        scope.Complete();
        return toReturn;
    }
}

public static int CountReadUncommitted<T>(this IQueryable<T> query)
{
    using (var scope = new TransactionScope(
        TransactionScopeOption.Required, 
        new TransactionOptions() { 
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
    {
        int toReturn = query.Count();
        scope.Complete();
        return toReturn;
    }
}

Bunu projemde kullanmak bağlantı havuzunun tamamen kullanılmasına ve sonuçta istisnalara neden olur. neden olduğunu anlayamıyorum. Bu sorunları olan başka biri var mı? Baska öneri?
Ben Tidman

1
Sorun yok Ben, bağlantı bağlamınızı DAİMA atmayı unutmayın.
Alexandre

İşlem kapsamını olası bir neden olarak dışlamak için sorunu daraltmayı başardı. Teşekkürler. Benim yapıcı içinde vardı bazı bağlantı yeniden deneyin şeyler ile ilgili bir şey vardı.
Ben Tidman

Kapsamın TransactionScopeOption olması gerektiğini düşünüyorum.Suppress
CodeGrue

@Alexandre Bunu başka bir ReadComended İşleminde yapsaydım ne olurdu? Örneğin ben veri kaydetmeye başlamak için bir işlem yumurtladı ama şimdi daha fazla veri sorgulama ve bu nedenle içinde bir ReadUncommitted işlem yumurtlama? Bu "Tamamlandı" olarak adlandırmak dış işlemimi de tamamlayacak mı? Nazikçe tavsiye :)
Jason Loki Smith

27

Büyük bir şeye ihtiyacınız varsa, her seferinde bir işlem kapsamına başlamaktan daha az müdahaleci bulmanın en iyi yolu, bu basit komutu çalıştırarak nesne bağlamınızı oluşturduktan sonra bağlantınızdaki varsayılan işlem yalıtım düzeyini ayarlamaktır:

this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx

Bu teknikle, bizim için bağlamı oluşturan ve bu komutu tüm bağlamımız için her seferinde çalıştıran basit bir EF sağlayıcısı oluşturabildik, böylece her zaman varsayılan olarak "taahhüt edilmemiş olarak okuyoruz".


2
Yalnızca işlem yalıtım düzeyinin ayarlanmasının herhangi bir etkisi olmayacaktır. Bunun herhangi bir etkisi olması için bir işlemin içinde çalışıyor olmanız gerekir. READ UNCOMMITTED durumları için MSDN belgeleri Transactions running at the READ UNCOMMITTED level do not issue shared locks. Bu, avantaj elde edebilmek için bir işlem içinde çalışmanız gerektiği anlamına gelir. ( msdn.microsoft.com/en-gb/library/ms173763.aspx adresinden alınmıştır ). Yaklaşımınız daha az müdahaleci olabilir, ancak bir işlem kullanmazsanız hiçbir şey başaramaz.
Doktor Jones

3
MSDN belgeleri şunları söylüyor: "SQL Server bağlantısıyla yayınlanan Transact-SQL deyimlerinin kilitleme ve satır sürüm oluşturma davranışını denetler." ve "İfadelerin diğer işlemler tarafından değiştirilmiş ancak henüz tamamlanmamış satırları okuyabileceğini belirtir." Yazdığım bu ifade HER SQL deyimlerini etkiler, bir işlemin içinde olsun ya da olmasın. Çevrimiçi ortamda insanlarla çelişmekten hoşlanmıyorum, ancak bu ifadeyi geniş bir üretim ortamında kullanmamıza dayanarak bu konuda açıkça yanılıyorsunuz. Bir şey varsaymayın, ONUN DENEYİN!
Frank.Germain

Onları denedim, bu işlem kapsamlarından birinde (ve eşleşen bir işlemde) sorgu yapılmamasının bir kilitlenme ile sonuçlanacağı yüksek yük ortamına sahibiz. Benim gözlemlerim bir SQL 2005 sunucusunda yapıldı, bu yüzden davranış o zamandan beri değişti bilmiyorum. Bu nedenle bunu tavsiye ederim; okunmamış bir yalıtım düzeyi belirtir ancak kilitlenme yaşamaya devam ederseniz, sorgularınızı bir işleme koymayı deneyin. Bir işlem oluşturmadan çıkmaza girmiyorsanız, yeterince adil.
Doctor Jones

3
@DoctorJones - Microsoft SQL Server ile ilgili olarak, tüm sorgular doğal olarak işlemlerdir. Açık bir işlem belirtmek, bir atomik iş birimi olarak düşünülebilmeleri için aynı işlemi 2 veya daha fazla ifadeyi gruplamak için bir araçtır . SET TRANSACTION ISOLATION LEVEL...Komutu bir bağlantı seviyesi varlıkla etkiler ve sorgu ipucu geçersiz kılınmadığı takdirde dolayısıyla (BU bağlantısı için) o andan itibaren yapılan tüm SQL ifadelerini etkiler. Bu davranış, en azından SQL Server 2000'den beri ve muhtemelen daha önce olmuştur.
Solomon Rutzky

5
@DoctorJones - Şuna bakın : msdn.microsoft.com/en-us/library/ms173763.aspx . İşte bir test. SSMS olarak, bir sorgu (# 1) açın ve komut satırından: CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);. Başka bir sorgu (# 2) açın ve komut satırından: SELECT * FROM ##Test;. SELECT, özel bir kilit kullanan sekme 1'deki hala açık işlem tarafından engellendiği için geri dönmez. SEÇİMİ # 2'den iptal edin. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDSekme # 2'de bir kez çalıştırın . Sekme # 2'de sadece SELECT'i tekrar çalıştırdığınızda geri gelecektir. ROLLBACKSekme # 1'de çalıştığınızdan emin olun .
Solomon Rutzky

21

Kesinlikle Okunmamış İşlem izolasyon düzeyini kullanmanın en iyi seçim olduğunu kesinlikle kabul ettim, ancak bir süre NOLOCK ipucunu yönetici veya müşterinin isteği ile kullanmak zorunda kaldınız ve buna karşı hiçbir neden kabul edilmedi.

Entity Framework 6 ile kendi DbCommandInterceptor uygulamasını şu şekilde uygulayabilirsiniz:

public class NoLockInterceptor : DbCommandInterceptor
{
    private static readonly Regex _tableAliasRegex = 
        new Regex(@"(?<tableAlias>AS \[Extent\d+\](?! WITH \(NOLOCK\)))", 
            RegexOptions.Multiline | RegexOptions.IgnoreCase);

    [ThreadStatic]
    public static bool SuppressNoLock;

    public override void ScalarExecuting(DbCommand command, 
        DbCommandInterceptionContext<object> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }
}

Bu sınıf hazır olduğunda, uygulama başlangıcında uygulayabilirsiniz:

DbInterception.Add(new NoLockInterceptor());

Ve NOLOCKgeçerli iş parçacığı için sorgulara ipucu eklemeyi koşullu olarak kapatın :

NoLockInterceptor.SuppressNoLock = true;

Russ

2
(? <tableAlias>] AS [Extent \ d +] (?! İLE (NOLOCK))) türetilmiş tabloya nolock eklenmesini önlemek için hataya neden olur. :)
Russ

SuppressNoLock'u iş parçacığı düzeyinde ayarlamak uygun bir yoldur, ancak booleanın ayarını kaldırmayı unutmak kolaydır, IDisposable'ı döndüren bir işlev kullanmalısınız, Dispose yöntemi bool'ü tekrar false olarak ayarlayabilir. Ayrıca, ThreadStatic async / await ile gerçekten uyumlu değil: stackoverflow.com/questions/13010563/…
Jaap

Veya, İZOLASYON SEVİYESİNİ kullanmayı tercih ederseniz: public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { if (!SuppressNoLock) command.CommandText = $"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;{Environment.NewLine}{command.CommandText}"; base.ReaderExecuting(command, interceptionContext); }
Adi

Veritabanı işlevlerine de nolock ekliyor. Fonksiyonlar nasıl önlenir?
Ivan Lewis

9

Üzerinde geliştirilmesi Doktor Jones 'in kabul edilen cevap ve kullanma PostSharp ;

İlk " ReadUncommitedTransactionScopeAttribute "

[Serializable]
public class ReadUncommitedTransactionScopeAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        //declare the transaction options
        var transactionOptions = new TransactionOptions();
        //set it to read uncommited
        transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
        //create the transaction scope, passing our options in
        using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
        {
            //declare our context
            using (var scope = new TransactionScope())
            {
                args.Proceed();
                scope.Complete();
            }
        }
    }
}

Sonra ne zaman ihtiyacınız olursa,

    [ReadUncommitedTransactionScope()]
    public static SomeEntities[] GetSomeEntities()
    {
        using (var context = new MyEntityConnection())
        {
            //any reads we do here will also read uncomitted data
            //...
            //...

        }
    }

Bir durdurucu ile "NOLOCK" ekleyebilmek de güzeldir ancak Oracle gibi diğer veritabanı sistemlerine bağlanırken çalışmaz.


6

Bu tur almak için veritabanında bir görünüm oluşturmak ve görünümün sorgusunda NOLOCK uygulayın. Daha sonra görüntüyü EF içinde bir tablo olarak ele alıyorum.


4

EF6'nın tanıtılmasıyla Microsoft, BeginTransaction () yönteminin kullanılmasını önerir.

EF6 + ve EF Core'da TransactionScope yerine BeginTransaction'ı kullanabilirsiniz

using (var ctx = new ContractDbContext())
using (var transaction = ctx.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
    //any reads we do here will also read uncommitted data
}

2

Hayır, gerçekten değil - Entity Framework aslında gerçek veritabanınızın üzerinde oldukça katı bir katmandır. Sorgularınız, öncelikle varlık modelinize yönelik ESQL - Entity SQL'de formüle edilmiştir ve EF birden çok veritabanı arka ucunu desteklediğinden, gerçekten "doğal" SQL'i doğrudan arka uçlarınıza gönderemezsiniz.

NOLOCK sorgu ipucu SQL Server'a özgü bir şeydir ve desteklenen diğer veritabanlarının hiçbirinde çalışmazlar (aynı ipucunu uygulamadılarsa - kesinlikle şüpheliyim).

üzüm posası


Bu yanıt güncel değil - diğerlerinin belirttiği gibi NOLOCK kullanabilirsiniz ve Database.ExecuteSqlCommand()veya kullanarak "yerel" SQL çalıştırabilirsiniz DbSet<T>.SqlQuery().
Dunc

1
@Dunc: downvote için teşekkürler - arada: Eğer gereken DEĞİL kullanmak (NOLOCK)zaten - bkz tekme için Kötü Alışkanlıklar - her yerde nolock koyarak - olduğu ÖNERİLMEZ her yerde bu kullanmak için - tam tersine!
marc_s

0

Bir seçenek saklı bir prosedür kullanmak (Ryan tarafından önerilen görünüm çözümüne benzer) ve daha sonra saklı prosedürü EF'den yürütmektir. Bu şekilde, saklı yordam kirli okuma yapar, EF sadece sonuçları verir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.