在SqlServer中,頻繁在同一個數據庫表同時進行讀寫的時候,會存在鎖的問題,也就是在前一個insert、update、delete事務操作完畢之前,你不能進行讀取,必須要等到操作完畢,你才能進行select操作,目的是為了防止並發操作而讀到臟數據,在SQL語句中,如果能容忍這種情況、加快查詢速度,可以忽略鎖進行查詢:
select * from [User] with(nolock)
但是如果你項目中使用EntityFramework,可以使用下面這段代碼進行nolock查詢:需要添加System.Transactions程序集的引用
//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(); }
改進,如果項目里有多處地方都需要nolock查詢,這段代碼就需要不斷的拷貝了,這時候需要考慮進行封裝,很明顯,外層代碼可以很容易的抽出來,但是,里面的代碼段是不確定的,如果封裝的話,執行的時候,就需要傳一個代碼片段進去,委托在這種情況就派上用場了,我們可以使用委托來改進一下,也就是查詢數據庫時候的邏輯代碼代由委托傳遞進去。
public static void NoLockInvokeDB(Action action) { var transactionOptions = new System.Transactions.TransactionOptions(); transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted; using (var transactionScope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions)) { try { action(); } finally { transactionScope.Complete(); } } }
使用的時候也很簡單:
NoLockInvokeDB(() =>
{ using (var db = new TicketDB()) { lst = db.User.ToList(); } });
如果你不太習慣Action這種微軟高度封裝的委托的寫法(其本質就是委托),可以繼續看下去原始委托的寫法:(我個人一直的觀點是,微軟喜歡搞高度封裝的東西,想讓程序員提高編程工作效率,但是,初入門的程序員因為不知道其中的原理,只會使用,所以就成了真真正正的代碼農民工,所以,我們在工作學習的過程中,不要因為使用而使用,多去探討其本質實現,對自己的提高有幫助。)
public class Helper { public void NoLockInvokeDB(EFdelegate d) { var transactionOptions = new System.Transactions.TransactionOptions(); transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted; using (var transactionScope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions)) { d(); transactionScope.Complete(); } } } public delegate void EFdelegate();
調用也非常簡單
EFdelegate d = new EFdelegate(() => { //這里寫傳遞的代碼段 });
如果不習慣這種匿名函數的寫法的話,那就寫全了。
protected void Page_Load(object sender, EventArgs e) { EFdelegate d = new EFdelegate(SonFun); } public void SonFun() { //這里寫傳遞的代碼片段 }
參考文獻:
http://stackoverflow.com/questions/926656/entity-framework-with-nolock
2018-12-28 Update
現在項目已經廢除了上述的NoLockInvokeDB的寫法了,改成下面這種使用方法。在DBContext類里定義NoLock、WithLock方法,實際上就是在執行你的業務SQL之前,先執行NoLock、WithLock操作
/// <summary> /// base.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;"); /// 設置后依然可以SaveChanges(),推薦需要SaveChanges()時先調用下WithLock() /// </summary> public void NoLock() { base.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;"); } /// <summary> /// base.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ COMMITTED;"); /// 這個是數據庫默認設置。 /// </summary> public void WithLock() { base.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ COMMITTED;"); }
使用的時候,也非常簡單。在查詢之前,先使用db.NoLock() 即可。
using (var db = new ProductDB()) { db.NoLock(); var data= db.Person.ToList(); }