Làm cách nào tôi có thể sử dụng NOLOCK
chức năng trên Entity Framework? Có phải XML là cách duy nhất để làm điều này?
Làm cách nào tôi có thể sử dụng NOLOCK
chức năng trên Entity Framework? Có phải XML là cách duy nhất để làm điều này?
Câu trả lời:
Không, nhưng bạn có thể bắt đầu một giao dịch và đặt mức cô lập để đọc không được cam kết . Điều này về cơ bản thực hiện giống như NOLOCK, nhưng thay vì thực hiện trên cơ sở mỗi bảng, nó sẽ thực hiện mọi thứ trong phạm vi giao dịch.
Nếu điều đó nghe giống như những gì bạn muốn, thì đây là cách bạn có thể thực hiện ...
//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();
}
Phương pháp mở rộng có thể làm cho điều này dễ dàng hơn
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;
}
}
Nếu bạn cần một cái gì đó lớn, cách tốt nhất mà chúng tôi thấy ít xâm phạm hơn so với thực tế bắt đầu một giao dịch mỗi lần, là chỉ cần đặt mức cách ly giao dịch mặc định trên kết nối của bạn sau khi bạn đã tạo bối cảnh đối tượng của mình bằng cách chạy lệnh đơn giản này:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/l Library / aa259216 (v = sql.80) .aspx
Với kỹ thuật này, chúng tôi đã có thể tạo một nhà cung cấp EF đơn giản tạo bối cảnh cho chúng tôi và thực sự chạy lệnh này mỗi lần cho tất cả bối cảnh của chúng tôi để chúng tôi luôn luôn "đọc không cam kết" theo mặc định.
Transactions running at the READ UNCOMMITTED level do not issue shared locks
. Điều này ngụ ý rằng bạn phải chạy trong một giao dịch để nhận được lợi ích. (lấy từ msdn.microsoft.com/en-gb/l Library / ms173763.aspx ). Cách tiếp cận của bạn có thể ít xâm phạm hơn, nhưng nó sẽ không đạt được bất cứ điều gì nếu bạn không sử dụng giao dịch.
SET TRANSACTION ISOLATION LEVEL...
lệnh ảnh hưởng đến một thuộc tính kết nối cấp và do đó ảnh hưởng đến tất cả các câu lệnh SQL được thực hiện từ thời điểm đó trở đi (đối với RẰNG kết nối), trừ khi ghi đè bởi một gợi ý truy vấn. Hành vi này đã xuất hiện từ ít nhất là SQL Server 2000 và có thể là trước đó.
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
. Mở một truy vấn khác (# 2) và chạy : SELECT * FROM ##Test;
. CHỌN sẽ không quay trở lại vì nó đang bị chặn bởi giao dịch vẫn đang mở trong tab # 1 đang sử dụng khóa độc quyền. Hủy CHỌN trong # 2. Chạy SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
một lần trong tab # 2. Chỉ chạy lại CHỌN trong tab # 2 và nó sẽ quay lại. Hãy chắc chắn để chạy ROLLBACK
trong tab # 1.
Mặc dù tôi hoàn toàn đồng ý rằng sử dụng cấp độ cách ly giao dịch Đọc không cam kết là lựa chọn tốt nhất, nhưng đôi khi bạn buộc phải sử dụng gợi ý NOLOCK theo yêu cầu của người quản lý hoặc khách hàng và không có lý do nào để chấp nhận điều này.
Với Entity Framework 6, bạn có thể triển khai DbCommandInterceptor của riêng mình như thế này:
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)");
}
}
}
Với lớp này tại chỗ, bạn có thể áp dụng nó khi bắt đầu ứng dụng:
DbInterception.Add(new NoLockInterceptor());
Và có điều kiện tắt thêm NOLOCK
gợi ý vào các truy vấn cho chuỗi hiện tại:
NoLockInterceptor.SuppressNoLock = true;
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); }
Nâng cao câu trả lời được chấp nhận của Doctor Jones và sử dụng PostSharp ;
Đầu tiên " ReadUncommitedTransactionScopeAttribution "
[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();
}
}
}
}
Sau đó, bất cứ khi nào bạn cần nó,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Có thể thêm "NOLOCK" bằng một thiết bị chặn cũng tốt nhưng sẽ không hoạt động khi kết nối với các hệ thống cơ sở dữ liệu khác như Oracle.
Để hoàn thành điều này, tôi tạo một khung nhìn trên cơ sở dữ liệu và áp dụng NOLOCK cho truy vấn của khung nhìn. Sau đó, tôi coi khung nhìn như một bảng trong EF.
Với việc giới thiệu EF6, Microsoft khuyên bạn nên sử dụng phương thức BeginTransaction ().
Bạn có thể sử dụng BeginTransaction thay vì TransactionScope trong EF6 + và EF Core
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
}
Không, không thực sự - Entity Framework về cơ bản là một lớp khá nghiêm ngặt trên cơ sở dữ liệu thực tế của bạn. Các truy vấn của bạn được xây dựng trong ESQL - SQL thực thể - trước hết được nhắm mục tiêu theo mô hình thực thể của bạn và vì EF hỗ trợ nhiều phụ trợ cơ sở dữ liệu, bạn thực sự không thể gửi SQL "gốc" trực tiếp đến phụ trợ của mình.
Gợi ý truy vấn NOLOCK là một điều cụ thể của Máy chủ SQL và sẽ không hoạt động trên bất kỳ cơ sở dữ liệu được hỗ trợ nào khác (trừ khi chúng cũng đã thực hiện cùng một gợi ý - điều mà tôi rất nghi ngờ).
Marc
Database.ExecuteSqlCommand()
hoặc DbSet<T>.SqlQuery()
.
(NOLOCK)
- xem Thói quen xấu để đá - đặt NOLOCK ở mọi nơi - KHÔNG ĐƯỢC KHUYẾN NGHỊ để sử dụng điều này ở mọi nơi - hoàn toàn ngược lại!