در SQL زمانی که رکوردها به صورت BULK INSERT ذخیره شوند و تعداد این رکوردها زیاد باشد SQL جدول مورد نظر را Lock میکند و تا زمانی که تمامی دیتاها وارد SQL نشوند نمیتوانید از آن جدول دیتاها را بخوانید (به صورت پیشفرض). اما در خود SQL میتوانید دیتاها را به صورت UnCommited از جداول بخوانیم. با این کار دیتاهایی که Commit نشده اند را فقط میتوان دریافت کرد و عملا منتظر نمی مانیم تا تمامی رکوردها وارد SQL شوند و سپس دستور ما اجرا شود. نحوه خواندن دیتاها به صورت UnCommited به این صورت است:

SELECT * FROM Categories WITH(NOLOCK)

نحوه خواندن دیتاها به صورت UnCommited به این صورت است که باید بعد از اسم جدول مربوطه کلمه (WITH(NOLOCK  را قرار دهیم و یا میتوانیم با اجرا کردن دستور زیر قبل از اجرای کوئری نحوه خواندن دیتاها را به صورت UnCommited  برای این تراکنش فعال کنیم.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

اما اگر بخواهیم همین کار را در EntityFramework انجام دهیم میتوانیم زمانی که دیتاها را از دیتابیس میخوانیم از TransactionScope استفاده کنیم  و دیتاها به صورت UnCommited دریافت شوند. برای فعال کردن این ویژگی میتوانیم به صورت زیر عمل کنیم:

using (var scope = new TransactionScope(TransactionScopeOption.Required,
                        new TransactionOptions()
                        {
                            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted
                        }))
{
    var result = _context.Categories.ToList();
    scope.Complete();
}

و اگر بخواهیم دیتاها را به صورت async دریافت کنیم باید پارامتر TransactionScopeAsyncFlowOption.Enabled را به سازنده کلاس TransactionScope ارسال کنیم و به صورت زیر عمل کنیم :

using (var scope = new TransactionScope(TransactionScopeOption.Required,
                        new TransactionOptions()
                        {
                            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted
                        },
                        TransactionScopeAsyncFlowOption.Enabled))
{
    var result = await _context.Categories.ToListAsync();
    scope.Complete();
}

میتوانیم برای سهولت در کار همین کارها را به صورت یک اکستنشن متد بنویسیم.

public static async Task<List<T>> ToListWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
{
    List<T> result = default;
    using (var scope = new TransactionScope(TransactionScopeOption.Required,
                            new TransactionOptions()
                            {
                                IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted
                            },
                            TransactionScopeAsyncFlowOption.Enabled))
    {
        result = await query.ToListAsync(cancellationToken);
        scope.Complete();
    }
    return result;
}

با این کار هر زمانی که بخواهیم دیتاها را به صورت UnCommited توسط EntityFramework بخوانیم میتوانیم به جای استفاده از ToListAsync از ToListWithNoLockAsync استفاده کنیم.

var categories = dbContext.Categories
                          .AsNoTracking()
                          .Where(a => a.IsDelete == false)
                          .ToListWithNoLockAsync();

همچنین میتوانید بدون ایجاد کردن TransactionScope این قابلیت را با استفاده از کد زیر اجرا کنید:

this.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

اگر شما قطعه کد بالا را اجرا کنید EntityFramework از یک TRANSACTION ISOLATION LEVEL برای یک پروسس استفاده میکند و از آنجاکه ProcessID در یک ریکوئست تغییر نمیکند, یکبار اجرای این کد برای هر درخواست کافی است.

نکته : اگر یکی از کوئری های شما به صورت WITH NOLOCK اجرا شود, ما بقی کوئری هایی بعدی نیز به همین صورت اجرا میشوند. به طور مثال در قطعه کد زیر دستور اول مقدار TRANSACTION ISOLATION LEVEL را به READ UNCOMMITED تغییر میدهد و دستور دومی نیز با همین TRANSACTION ISOLATION LEVEL اجرا میشود.

var categoriesNoLock = await _context.Categories.ToListWithNoLockAsync();//READ UNCOMMITED
var categories = await _context.Categories.ToListAsync();//READ UNCOMMITED

در این ریپازیتوری میتوانید چند نمونه از متدهای پیاده سازی شده که دیتاها را به صورت UnCommited دریافت میکنند را مشاهده کنید.

;)

Powered by Froala Editor