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

来源:http://www.cnblogs.com/xuanqust/archive/2017/01/03/6244090.html
-Advertisement-
Play Games

在一次請求中,即一個線程內,若是用到EF數據上下文對象,就創建一個,這也加是很多人的代碼中習慣在使用上下文對象時,習慣將對象建立在using中,也是為了儘早釋放上下文對象, 但是如果有一個業務邏輯調用了多個dal層的方法,交互資料庫多次,這樣效率會低一些,而且在使用EF的情況下,我們通常把SaveC ...


  在一次請求中,即一個線程內,若是用到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,很贊。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • linux用戶分類 超級用戶:UID=0,root 普通用戶:UID 500起,由超級用戶或具有超級用戶許可權的用戶創建的用戶。 虛擬用戶:UID 1—499,為了滿足文件或服務啟動的需要而存在,一般都不能登錄,只是傀儡用戶。 用戶關聯的四個文件: /etc/passwd:用戶的賬號文件 /etc/s... ...
  • 準備工作: 實現lnmp環境 給php添加模塊,so庫 下載擴展包:memcache-2.2.5.tgz wget http://pecl.php.net/get/memcache-2.2.5.tgz ...
  • 最近在寫一個批量巡檢工具,利用ansible將腳本推到各個機器上執行,然後將執行的結果以json格式返回來。 如下所示: # ansible node2 -m script -a /root/python/health_check.py 然後將結果重定向到一個文本文件中,再通過另外一個腳本,對該文本 ...
  • .Net程式員們每天都在和Object在打交道 如果你問一個.Net程式員什麼是Object,他可能會信誓旦旦的告訴你"Object還不簡單嗎,就是所有類型的基類" 這個答案是對的,但是不足以說明Object真正是什麼 在這篇文章我們將會通過閱讀CoreCLR的源代碼瞭解Object在記憶體中的結構和 ...
  • 在接著寫Asp.Net WebApi核心對象解析(下篇)之前,還是一如既往的扯扯淡,元旦剛過,整個人還是處於暈的狀態,一大早就來處理系統BUG,簡直是坑爹(好在沒讓我元旦趕過來該BUG),隊友挖的坑,還讓我含著淚去填。改BUG前看類看隊友寫的代碼,這裡就不評價了,反正是邊改邊罵,我的嘴巴就沒停過,作 ...
  • 要想學會對象編程,首先要掌握面向對象編程的原則,方法,模式和技巧,並遵循它們來寫程式。這樣程式健壯,易於擴展,易維護。 面向對象編程的原則: 單一職責原則:一個對象要專註的做一件事; 開放封閉原則:開放擴展,封閉修改;(生成文件中,如果修改只要修改裡面的dll文件就可以了,不需要重新編譯生成安裝等) ...
  • 註: 調用是可使用Word.Application wdApp = GetWordApplication(true); 獲取該進程中的所有文檔可用:Word.Documents docs = wdApp.Documents; 轉載請註明出處,謝謝! ...
  • 在用C#開發Web應用時有個痛點,就是本機用VS開啟Web應用調試時外部機器無法訪問此Web應用。這裡將會介紹如何通過設置允許區域網和外網機器訪問本機的Web應用。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...