2014年1月29日 星期三

C# DbContext運用

DbContext是一個執行個體,表示工作單位和儲存機制模式的組合,使其可用來從資料庫查詢並將變更群組在一起,然後這些變更會當做一個單位寫回存放區。 DbContext 在概念上類似於 ObjectContext。

下面是微軟的說明:
DbContext 通常會搭配衍生型別使用,該型別包含模型根實體的 DbSet<TEntity> 屬性。 當建立衍生類別的執行個體時,這些集合會自動初始化。 可以修改這個行為,方法是將 SuppressDbSetInitializationAttribute 屬性 (Attribute) 套用到整個衍生內容類別或是此類別的個別屬性 (Property)。 支援此內容的實體資料模型可以使用幾個方式來指定。 當使用 Code First 方法時,衍生內容上的 DbSet<TEntity> 屬性會用來依照慣例建立模型。 可覆寫受保護的 OnModelCreating 方法來調整此模型。 如果想要對 Model First 方法所使用的模型擁有更多控制權,請從 DbModelBuilder 明確建立 DbCompiledModel,並將這個模型傳遞給其中一個 DbContext 建構函式。 當使用 Database First 或 Model First 方法時,可以使用 Entity Designer 建立實體資料模型 (或是藉由手動建立 EDMX 檔案),然後可以使用實體連接字串或 EntityConnection 物件指定這個模型。 可以使用幾個方法來指定資料庫的連接 (包括資料庫的名稱)。 如果從衍生內容呼叫無參數的 DbContext 建構函式,則會使用衍生內容的名稱在 app.config 或 web.config 檔案中尋找連接字串。 如果找不到連接字串,則會將此名稱傳遞給 Database 類別上註冊的 DefaultConnectionFactory。 然後連接 Factory 會使用此內容名稱當做預設連接字串中的資料庫名稱 (這個預設連接字串會指向 本機電腦上的 .\SQLEXPRESS,除非註冊了不同的 DefaultConnectionFactory)。如果不要使用衍生的內容名稱,也可以明確指定連接/資料庫名稱,方法是將此名稱傳遞給其中一個採用字串的 DbContext 建構函式。 也可以使用 "name=myname" 格式傳遞名稱,這樣必須在組態檔中找到此名稱,否則將會擲回例外狀況。 請注意,app.config 或 web.config 檔案中找到的連接可以是一般資料庫連接字串 (不是特殊的 Entity Framework 連接字串),此時 DbContext 將會使用 Code First。 但是,如果組態檔中找到的連接是特殊的 Entity Framework 連接字串,則 DbContext 將會使用 Database/Model First,而且會使用連接字串中指定的模型。 也可以使用現有或明確建立的 DbConnection 來取代資料庫/連接名稱。 DbModelBuilderVersionAttribute 可以套用至衍生自 DbContext 的類別,以便在建立模型時設定此內容所使用的慣例版本。 如果未套用任何屬性,則會使用最新版本的慣例。

在 ASP.NET MVC 應用程式中使用 Entity Framework 時,DbContext(或 ObjectContext)物件的壽命。

在使用 DbContext 時,大部分用using來確保資源盡快回收,例如:

using (var db = new NorthwindEntites())
{
    // 操作 db 的 entity 集合
}

因此大部分是讓 context 物件與 HTTP request 被C#一起釋放,也就是 one context per request:在 request 開始時建立 DbContext 物件,並且在 request 即將結束時摧毀 context 物件。

一個 Request 配一個 Context

由於 ASP.NET MVC 的  Controller 物件的壽命大約等同一個 request 的壽命,所以一種簡單的作法是在我們的 Controller 類別中建立和摧毀 context 物件。參考底下的程式片段:

public class HomeController : Controller
{
    private NorthwindEntities db = new NorthwindEntities();

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}

這個HomeController類別的程式碼,包括建立 context 物件(NorthwindEntites)和 Dispose 方法,全都是 Visual Studio 2012 的 Add Controller 功能幫我產生的,參考下圖:


所有的 Controller 都會用到同一個 DbContext 物件,則可以寫一個 Controller 基礎類別,例如 MyControllerBase,並將 DbContext 變數宣告在 MyControllerBase 類別中,成為其物件成員。

除了讓 context 成為 Controller 物件的成員,另一方法是在 Application 物件的 BeginRequest 事件中建立 context 並存入目前的 HttpContext 的 Items 集合,並且在 EndRequest 事件中摧毀 context 物件,例如:

protected void Application_BeginRequest()
{
    var db = new Models.MoneyUti();
    HttpContext.Current.Items["Northwind"] = db;
}

protected void Application_EndRequest()
{
    var db = HttpContext.Current.Items["MoneyUti"as Models.MoneyUti;
    if (db != null)
    {
        db.Dispose();
        db = null;
    }
}

在 Controller 中取出 context 物件可以如下的方式撰寫:

public ActionResult Index()
{
    var db = this.HttpContext.Items["Northwind"as MoneyUti;
    return View(db.Customers.ToList());
}

上面的程式商業邏輯層中的所有 Business Objects 也可以共用同一個的 DbContext 物件,並享有跨 BOs 交易管理的便利。如果ASP.NET 應用程式會在一個 request 生命週期中建立多條執行緒來分頭(並行)執行多個資料操作,就會有執行上的問題,要另外寫程式碼來處理 synchronization。

對 ASP.NET 應用程式來說,DbContext(或 ObjectContext)的生命週期控制模式通常就是One context per method和 One context per request兩種。如果是 Windows Forms 或 WPF 應用程式,則可以採用 one context per form/window 的方式。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

沒有留言:

張貼留言