首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

實體框架在 N 層架構中的整合實戰:從資料層到 WPF 被動視圖的完整範例

     編輯:關於.NET

本文基於 ADO.NET Entity Framework 預發布版本撰寫,展示如何在採用 WCF + WPF + MVP (Passive View) 的經典 N 層架構中,無縫整合 Entity Framework。架構涵蓋:資料庫層、資料存取層 (EDM)、領域模型層、業務邏輯層、服務層 (WCF)、表現層、被動 UI 層 (WPF)。核心挑戰在於跨層傳遞實體時的序列化、變更追蹤斷點重連、以及並發控制。

架構總覽

N 層架構高階圖

  • UI 層 (WPF):純粹的視圖,透過資料繫結顯示資料,引發事件(如搜尋、儲存)。
  • 表現層:實作 MVP 中的 Presenter,處理視圖事件、協調服務呼叫、轉換資料格式。
  • 服務層 (WCF):定義服務契約,跨進程/網路邊界傳遞 DataContract 實體。
  • 業務層:核心業務邏輯與 LINQ to Entities 查詢,持有 ObjectContext 管理變更追蹤。
  • 資料層:SQL Server + Entity Data Model (EDM)。

關鍵設計決策:實體與 ObjectContext 的分離

  • 實體跨層傳遞:透過 WCF [DataContract] 序列化實體,ObjectContext 不序列化、不跨層。UI 層拿到的是純 POCO-like 實體 (Detached 狀態)。
  • 變更追蹤留在業務層:UI 修改實體後回傳服務層 → 業務層,業務層負責 Attach 實體回 ObjectContext、標記修改狀態、呼叫 SaveChanges。
  • 自動圖形序列化 (.NET 3.5 SP1+):父實體攜帶導覽屬性集合(如 Order → OrderDetails)一同序列化傳遞。

建立模型:命名慣例與 EDM 精修

EDM 精靈從資料庫產生模型

建議命名慣例:

  • EntityType 用單數:Customer、Order — 代表單一實體藍圖。
  • EntitySet 用複數:Customers、Orders — 代表集合查詢入口。
// LINQ 查詢語意清晰
var q = from c in context.Customers  // EntitySet (複數)
        select c;                    // 回傳 Customer (單數) 實體
List<Customer> list = q.ToList();

EDM 設計師中選取實體,在屬性視窗修改 Name (EntityType) 與 EntitySet Name:

修改 EntityType Name 為單數

執行流程:從 WPF 視圖到資料庫查詢

1. 視圖引發搜尋事件

// NWUI/CustomerSearchView.xaml.cs
private void btnSearch_Click(object sender, RoutedEventArgs e)
{
    FindCustomerSearchResults?.Invoke(); // 引發事件,不直接呼叫服務
}

2. Presenter 監聽事件、呼叫 WCF 服務


// NWPresentation/CustomerSearchPresenter.cs
public void view_FindCustomerSearchResults()
{
    if (string.IsNullOrEmpty(view.CompanyNameCriteria)) return;

    using (var svc = new NWServiceClient()) // WCF Proxy
    {
        // 服務回傳 IList<Customer> (DataContract 實體)
        IList<Customer> customers = svc.FindCustomerList(view.CompanyNameCriteria);
        view.CustomerSearchResultsList = customers; // 設定視圖屬性,觸發 WPF 資料繫結
    }
}

3. 服務層轉呼叫業務層


// NWBusinessManagers/CustomerManager.cs
public class CustomerManager
{
    private readonly NWEntities context = new NWEntities(); // ObjectContext 為類別欄位,生命週期跟隨 Manager

    public List<Customer> FindCustomerList(string companyName)
    {
        var q = from c in context.Customers
                where c.CompanyName.StartsWith(companyName)
                select c;
        return q.ToList(); // 執行查詢,實體附著於 context 追蹤
    }
}

持久化修改:Detached 實體重新 Attach 與狀態標記

當使用者編輯客戶並儲存,實體經 WCF 傳回業務層時已處於 Detached 狀態:

// CustomerManager.cs
public void UpdateCustomer(Customer customer)
{
    // 1. 重新附著到 ObjectContext
    context.Attach(customer);

    // 2. 標記所有屬性為 Modified (自訂擴充方法)
    customer.SetAllModified(context);

    // 3. 儲存變更,產生 UPDATE T-SQL
    context.SaveChanges();
}

SetAllModified 擴充方法實作


public static void SetAllModified<T>(this T entity, ObjectContext context)
    where T : IEntityWithKey
{
    var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
    var propertyNames = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata
                         .Select(pn => pn.FieldType.Name);

    foreach (var propName in propertyNames)
        stateEntry.SetModifiedProperty(propName);
}

替代方案:Refresh 合併策略


// ClientWins:以用戶端值覆蓋資料庫 (Last-in-wins)
context.Refresh(RefreshMode.ClientWins, customer);
context.SaveChanges();

// StoreWins:放棄用戶端修改,重新載入資料庫最新值
context.Refresh(RefreshMode.StoreWins, customer);

並發控制:ConcurrencyMode=Fixed 與異常處理

在 EDM 中將關鍵屬性 (如 Timestamp、RowVersion 或業務欄位) 的 ConcurrencyMode 設為 Fixed。EF 會在 UPDATE/DELETE 的 WHERE 子句加入原始值比對。

OptimisticConcurrencyException 範例


try
{
    context.SaveChanges();
}
catch (OptimisticConcurrencyException ex)
{
    // 記錄日誌、採用 ClientWins 覆蓋或 StoreWins 重新載入提示使用者
    context.Refresh(RefreshMode.ClientWins, customer); // Last-in-wins
    context.SaveChanges();
}

刪除與新增實體

刪除


context.Attach(customer);
context.DeleteObject(customer);
context.SaveChanges();

新增


context.AddToCustomers(customer); // 或 context.AddObject("Customers", customer)
context.SaveChanges();

ObjectContext 生命週期管理原則

原則說明
每個業務管理類別持有一個 ObjectContext在 Manager 建構函式建立,Dispose 時釋放。
不要長期保持同一 ObjectContext身份解析會導致快取資料過舊、效能下降、多執行緒衝突。
Web/WCF 服務採用 Per-Call/Per-Request每次呼叫建立新 Context,用完即棄,確保資料最新。

原始碼下載

完整可執行範例 (含 NorthwindEF 資料庫腳本、WPF UI、WCF 服務、業務層):

DataPoints2008_07.exe (3.5 MB)

總結

Entity Framework 在 N 層架構中的核心價值在於:

  1. 變更追蹤機制 留在業務層,UI 層保持輕量。
  2. LINQ to Entities 提供強型別查詢,編譯期檢查。
  3. Detach/Attach 模式支援跨層實體傳遞與重新追蹤。
  4. 並發控制 內建樂觀並發支援,配合 WCF 邊界無縫運作。

掌握這些模式,即能在企業級分層應用中充分發揮 EF 的生產力優勢,同時維持架構乾淨、可測試、可維護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved