ฉันจะใช้NOLOCK
ฟังก์ชันบน Entity Framework ได้อย่างไร XML เป็นวิธีเดียวที่จะทำสิ่งนี้หรือไม่?
ฉันจะใช้NOLOCK
ฟังก์ชันบน Entity Framework ได้อย่างไร XML เป็นวิธีเดียวที่จะทำสิ่งนี้หรือไม่?
คำตอบ:
ไม่มี แต่คุณสามารถเริ่มต้นการทำธุรกรรมและการตั้งค่าระดับการแยกการอ่าน uncommited สิ่งนี้จะทำเช่นเดียวกับ NOLOCK แต่แทนที่จะทำตามตารางต่อมันจะทำทุกอย่างภายในขอบเขตของการทำธุรกรรม
หากดูเหมือนว่าสิ่งที่คุณต้องการนี่คือวิธีที่คุณสามารถทำมันได้ ...
//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();
}
วิธีการขยายสามารถทำให้ง่ายขึ้น
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;
}
}
หากคุณต้องการสิ่งที่มีขนาดใหญ่วิธีที่ดีที่สุดที่เราพบว่าน้อยกว่าการเริ่มต้นสโคปธุรกรรมในแต่ละครั้งคือการตั้งค่าระดับการแยกธุรกรรมเริ่มต้นในการเชื่อมต่อของคุณหลังจากที่คุณสร้างบริบทวัตถุโดยใช้คำสั่งง่าย ๆ นี้
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
ด้วยเทคนิคนี้เราสามารถสร้างผู้ให้บริการ EF อย่างง่ายที่สร้างบริบทให้เราและเรียกใช้คำสั่งนี้ทุกครั้งสำหรับบริบททั้งหมดของเราเพื่อให้เราอยู่ใน "read uncommitted" โดยค่าเริ่มต้น
Transactions running at the READ UNCOMMITTED level do not issue shared locks
สำหรับอ่านปราศจากข้อผูกมัดรัฐ หมายความว่าคุณต้องทำงานภายในธุรกรรมเพื่อรับผลประโยชน์ (นำมาจากmsdn.microsoft.com/en-gb/library/ms173763.aspx ) วิธีการของคุณอาจล่วงล้ำน้อยลง แต่จะไม่ประสบความสำเร็จหากคุณไม่ใช้ธุรกรรม
SET TRANSACTION ISOLATION LEVEL...
คำสั่งมีผลต่อคุณสมบัติการเชื่อมต่อระดับและด้วยเหตุนี้ส่งผลกระทบต่อคำสั่ง SQL ทั้งหมดที่ทำจากจุดนั้นเป็นต้นไป (สำหรับการเชื่อมต่อนั้น) เว้นแต่แทนที่โดยคำใบ้แบบสอบถาม พฤติกรรมนี้มีมาตั้งแต่อย่างน้อย SQL Server 2000 และเป็นไปได้ก่อน
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
และเรียกใช้: เปิดการสอบถามอื่น (# 2) SELECT * FROM ##Test;
และการทำงาน: SELECT จะไม่ส่งคืนเนื่องจากถูกบล็อกโดยธุรกรรมที่เปิดอยู่ในแท็บ # 1 ที่ใช้การล็อกแบบเอกสิทธิ์ ยกเลิกการเลือกใน # 2 ทำงานSET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
ครั้งเดียวในแท็บ # 2 เรียกใช้เพียงเลือกอีกครั้งในแท็บ # 2 และมันจะกลับมา อย่าลืมรันROLLBACK
ในแท็บ # 1
แม้ว่าฉันจะเห็นด้วยอย่างแน่นอนว่าการใช้ระดับการแยกธุรกรรมแบบไม่มีข้อผูกมัดเป็นทางเลือกที่ดีที่สุด แต่บางครั้งคุณถูกบังคับให้ใช้คำใบ้ NOLOCK ตามคำขอของผู้จัดการหรือลูกค้าและไม่มีเหตุผลใดที่ยอมรับได้
ด้วย Entity Framework 6 คุณสามารถใช้ DbCommandInterceptor ของตัวเองเช่นนี้:
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)");
}
}
}
เมื่อใช้คลาสนี้คุณสามารถสมัครได้ตั้งแต่เริ่มต้นแอปพลิเคชัน:
DbInterception.Add(new NoLockInterceptor());
และปิดการเพิ่มNOLOCK
คำใบ้ลงในแบบสอบถามแบบมีเงื่อนไขสำหรับเธรดปัจจุบัน:
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); }
เสริมสร้างบนหมอโจนส์ตอบรับ 's และการใช้PostSharp ;
ครั้งแรก " 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();
}
}
}
}
จากนั้นเมื่อใดก็ตามที่คุณต้องการ
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
การสามารถเพิ่ม "NOLOCK" ด้วย interceptor ก็ดี แต่จะไม่ทำงานเมื่อเชื่อมต่อกับระบบฐานข้อมูลอื่นเช่น Oracle เช่นนี้
รับรอบนี้ฉันสร้างมุมมองในฐานข้อมูลและใช้ NOLOCK ในแบบสอบถามของมุมมอง ฉันจึงมองว่าเป็นตารางภายใน EF
ด้วยการแนะนำของ EF6 นั้น Microsoft แนะนำให้ใช้วิธี BeginTransaction ()
คุณสามารถใช้ BeginTransaction แทน TransactionScope ใน EF6 + และ 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
}
ไม่ไม่จริง - Entity Framework นั้นเป็นชั้นที่ค่อนข้างเข้มงวดเหนือฐานข้อมูลจริง ข้อความค้นหาของคุณถูกกำหนดใน ESQL - เอนทิตี SQL - ซึ่งเป็นเป้าหมายแรกที่มีต่อโมเดลเอนทิตีของคุณและเนื่องจาก EF รองรับแบ็กเอนด์ฐานข้อมูลหลายตัวคุณไม่สามารถส่ง SQL "ดั้งเดิม" ไปยังแบ็กเอนด์ของคุณได้โดยตรง
คำแนะนำการสืบค้น NOLOCK เป็นสิ่งเฉพาะของ SQL Server และจะไม่ทำงานกับฐานข้อมูลอื่น ๆ ที่รองรับ (เว้นแต่ว่าพวกเขาจะใช้คำใบ้เดียวกัน - ซึ่งฉันสงสัยอย่างมาก)
มาร์ค
Database.ExecuteSqlCommand()
DbSet<T>.SqlQuery()
(NOLOCK)
ต่อไป - ดูBad Habits to kick - วาง NOLOCK ทุกที่ - ไม่แนะนำให้ใช้มันในทุกที่ - ค่อนข้างตรงกันข้าม!
ทางเลือกหนึ่งคือการใช้กระบวนงานที่เก็บไว้ (คล้ายกับโซลูชันมุมมองที่เสนอโดย Ryan) จากนั้นดำเนินการขั้นตอนการจัดเก็บจาก EF วิธีนี้ขั้นตอนการจัดเก็บจะดำเนินการอ่านสกปรกในขณะที่ EF เพียงส่งผลลัพธ์