程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> EF上下文對象線程內唯一性與優化,ef上下文對象線程

EF上下文對象線程內唯一性與優化,ef上下文對象線程

編輯:關於.NET

EF上下文對象線程內唯一性與優化,ef上下文對象線程


  在一次請求中,即一個線程內,若是用到EF數據上下文對象,就創建一個,這也加是很多人的代碼中習慣在使用上下文對象時,習慣將對象建立在using中,也是為了盡早釋放上下文對象, 但是如果有一個業務邏輯調用了多個dal層的方法,交互數據庫多次,這樣效率會低一些,而且在使用EF的情況下,我們通常把SaveChange這個方法提到業務邏輯層(下文中會提到),不保證同一個業務邏輯使用的是同一個上下文對象,事務,工作單元模式將無法實現。而且可能造成數據混亂,每次創建的對象執行相應的數據庫操作,與此同時,同一次的請求可能包含對數據的不同操作。其他的EF對象內獲得的數據可能已經是“過期”的了。即這個數據已經變動過。這就是髒讀。

        為了解決這個問題,關鍵就是上下文對象的創建問題。

        這裡首先想到單例模式,不過在這裡,不適合用,原因是使用單例模式,會使EF對象得不到及時的資源釋放。想象一下,無數個請求對數據庫的訪問,DbContext對象容器無數次增加對Model對象的Attach監控,內存就爆了。

        優化就是折中的過程,所以第二種方式考慮保證在線程內對象唯一,對於每一個請求使用同一個上下文。如何保證呢,通過微軟ASP機制線程相關的HttpContext對象以及CallContext對象。前面一篇文章中說過,HttpContext機制其實就是依靠CallContext對象實現的。先來看使用CallContext解決這個問題

你可以這樣做,在網站Common中添加處理類:

 

[csharp] view plain copy    在CODE上查看代碼片派生到我的代碼片
  1. /// <summary>  
  2.    /// 用來創建EF上下文對象,且保證線程內唯一。  
  3.    /// </summary>  
  4.    public class DbContextFactory  
  5.    {  
  6.        //DbContext在System.Data.Entity;中,不過這裡直接只引用這一個不行,還有EF其他的一些NameSpace所以直接添加一個實體模型,所有引用都進來了,然後再把模型刪了  
  7.        public static DbContext GetDbContext()  
  8.        {  
  9.            DbContext dbContext = (DbContext)CallContext.GetData("dbContext");  
  10.            if (dbContext == null)  
  11.            {  
  12.                dbContext = new WebEntities();  
  13.                CallContext.SetData("dbContext", dbContext);  
  14.            }  
  15.            return dbContext;  
  16.        }  
  17.    }  

           是不是很像緩存的使用策略。

           仔細思考一陣後發現,上面使用CallContext來存儲有什麼問題?就是說上面是把上下文對象依賴於一個線程。那麼由於線程池的存在,線程在處理完一個請求之後,並沒有被銷毀,存儲在CallContext中的上下文對象也一直存在,如果是下一次拿出這個線程去處理另一個請求,這個上下文對象其實也在不斷的膨脹,只不過比全局的膨脹的稍微慢一些。而且,有時候一個線程並不一定是拿去處理請求了,如果是服務器拿去處理其他的業務,那就可能引發一些其他的問題。

        所以,改進一下上面的辦法,借鑒一下J2EE的hibernate和mybatis,在DbContextFactory中添加一個remove方法,在業務邏輯層中每次請求使用完上下文之後,就把它從線程中移除。

        解決了,可是這辦法實在是。。。那如果我一次請求要調幾次業務邏輯呢,還是要創建多次上下文。而且這樣手動管理的方式,讓人痛苦。相信也是由於這個原因,在spring+hibernate中大家也是更願意用HibernateTemplate而不是HibernateDaoSupport。

        其實我們還有更好的辦法,在HttpContext中有一個Items屬性,它也可以用來保存key-value,這就完美了,一次請求正好對應著一個HttpContext,請求結束,它自動釋放,EF上下文也就不存在了。把上面代碼中的CallContext改為HttpContext.Current.Items,OK。

 

[csharp] view plain copy    在CODE上查看代碼片派生到我的代碼片
  1. public static DbContext DbContext()  
  2.         {  
  3.             DbContext dbContext = HttpContext.Current.Items["dbContext"] as DbContext;  
  4.             if (dbContext == null)  
  5.             {  
  6.                 dbContext = new WebEntities();  
  7.                 HttpContext.Current.Items["dbContext"] =  dbContext;  
  8.             }  
  9.             return dbContext;  
  10.         }  

 

 

 

          再說SavaChanges這個方法,我們現在可以做到EF上下文創建的優化,那麼它對數據庫的交互呢?這是我們寫了無數次的方法:

 

[csharp] view plain copy    在CODE上查看代碼片派生到我的代碼片
  1. public int AddUser(User user)  
  2.         {  
  3.             context.Add(user);  
  4.             return context.SaveChanges();  
  5.         }  


          當我們使用一個業務邏輯復雜的方法中,它可能需要使用到多個dal層對象或者說調用多次dal層的方法,上面的寫法,調幾次,EF就與數據庫交互了幾次,效率還是很低。那我們何不把與數據庫的交互方法SaveChanges()提到bll層來調用,由bll層方法來調用,一次的業務邏輯,只交互一次,形成一種工作單元模式。

 

          那麼怎麼提取,由於我們上下文對象在請求內唯一,那麼就再簡單不過了。

 

[csharp] view plain copy    在CODE上查看代碼片派生到我的代碼片
  1. public class DbSession  
  2. {  
  3.     public static int SaveChanges()  
  4.     {  
  5.         return DbContextFactory.GetDbContext().SaveChanges();  
  6.     }  
  7. }  

 

 

          為什麼把這個類名取為DbSession,學習JavaEE的朋友可能馬上想到了MyBatis,Hibernate,我們封裝了一個對數據庫的單元操作,與數據庫進行交互,就是一次與數據庫的會話。

        另外,我剛接觸EF的時候就有這個疑問,EF如果做到事務的處理,用TransactionScope或DbConnection?大可不必,如果我們把SaveChanges()提到業務邏輯層,就組成了一個事務單元,再聯想一下spring,為什麼會把聲明式事務放在Service層而不是Dao層,而且SaveChanges()這個方法其實本身事務的特性,如果保持了上下文對象的唯一性,間接也是完成了事務單元。

 

===========================2016-11-7===========================

        最近在MVC裡面用了一下NHibernate,仍然需要像管理EF上下文一樣管理Session對象,同樣,我們也可以把它"緩存"在HttpContext中,但是NHibernate已經幫我們完成了類似的工作。詳情參見http://www.cnblogs.com/13yan/archive/2013/05/17/3083552.html。作者還給大家提供了一個NHibernateHelper,很贊。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved