適合印表機的版本      送出     
按一下以給予評分及指教
Related Articles
ADO.NET 資料服務架構 (ADO.NET Data Services Framework) 的目標,是要建立一個簡單的 REST 架構,以輕鬆地公開及取用以資料為中心的服務。

By Elisa Flasko 以及 Mike Flasko (8 月 2008)
我們要向您介紹 BizTalk Server 2006 R2 中的 EDI 功能,並說明結構描述的建立、文件的對應、EDI 的傳遞與傳輸,以及例外的處理。

By Mark Beckner (8 月 2008)
本文節錄自 Laurence Moroney 即將出版的新書,作者在文中說明了 Silverlight 動畫的基本概念,以及 Expression Blend 所提供的動畫工具。

By Laurence Moroney (8 月 2008)
我們使用 InkPresenter 建置的 Silverlight 2.0 應用程式,可以讓使用者針對預先定義的影像集合塗鴉註解、執行手寫辨識,並將註解和辨識的文字儲存到伺服器端資料庫中。

By Julia Lerman (8 月 2008)
More ...
Articles by this Author
Entity Framework 是熱門話題,但您如何確知何時該使用 EntityClient、Object Service、Entity SQL 或 LINQ?此文章將為您解答。

By John Papa (May 2008)
LINQ to Objects 與 LINQ to Entities 有許多的標準查詢運算子,這些運算子會操作序列 (Sequence),以執行各式各樣的作業。

By John Papa (March 2008)
Entity Framework 賦予開發人員更大的彈性,使他們能夠捨棄關聯式資料模型,而改以概念模型做為其設計中心。首先,您必須先了解設計實體資料模型的方法。John Papa 將逐步引導您完成這項程序。

By John Papa (February 2008)
這個月,John Papa 要探討開發行動應用程式,來存取應用程式伺服器上的資料。

By John Papa (January 2008)
WPF 是 .NET Framework 3.0 最重要的新技術之一。本月,John Papa 將介紹其資料繫結功能。

By John Papa (December 2007)
在 T-SQL 中,檢視和衍生資料表的功用相似,而且各有優點。但是在處理 SQL Server 2005 時,還有第三個選擇,就是使用通用資料表運算式 (Common Table Expressions,CTE)。且待 John Papa 分曉。

By John Papa (October 2007)
ADO.NET 當中全新的 Entity Framework 將讓您利用物件模型來操控資料。且待 John Papa 分曉。

By John Papa (July 2007)
如 John Papa 所解釋,SQL Server Management Objects 提供強大的工具集,可供開發人員備份和還原資料庫,以及發出 DDL 命令。

By John Papa (June 2007)
More ...
Popular Articles
Howard Dierking 與 C++ 發明人 Bjarne Stroustrup 對談,內容包括語言的狂熱份子、程式語言的進化,以及展望程式設計的未來。

By Howard Dierking (April 2008)
本文將透過簡短的語言介紹和程式碼範例,來說明以 CLR 語言的各種語言架構。

By Joel Pobar (May 2008)
在本文中,作者使用 Microsoft 2007 Office system 中的文件資訊面板,來操作來自 Office 文件的中繼資料,以獲得更佳的尋找及管理功能。

By Ashish Ghoda (April 2008)
使用 Silverlight 製做動畫比您想像中簡單。在本文中我們將建立一個 3D 應用程式,這個應用程式會使用 XAML、C# 來摺疊出多面體 (Polyhedron),並模擬 DirectX 數學庫 (Math Library)。

By Declan Brennan (April 2008)
More ...
Read the Blog
One of the neat things about XAML is that you can not only declare your objects using an XML syntax, but that you can define transformations to rotate, move, and skew your objects. In the August 2008 issue of MSDN Magazine, in an article adapted from his upcoming book Introducing Microsoft Silverlight ...
Read more!
Microsoft has a long history of introducing new features to shipped products, often under the banner of Power Toys or Power Tools. In the August 2008 issue of MSDN Magazine, Brian Randell takes you on a tour of some useful tools for ...
Read more!
Designing software is often an exercise in managing complexity. You can take steps to limit the complexity of any given class by only assigning it a discrete set of responsibilities, applying a concept known as object role stereotypes. In the August 2008 issue of MSDN Magazine, Jeremy Miller explains ...
Read more!
When you evaluate any new technology, pattern, or strategy, you have to consider how that new piece of the puzzle is going to mesh with your existing application architecture. With the Entity Framework, integration is not a problem. In the July 2008 issue of MSDN Magazine, John Papa demonstrated ...
Read more!
Electronic Document Interchange (EDI) encompasses the largest share of real-world business-to-business commerce—nearly 90 percent of the current market—and is growing rapidly year over year. In the August 2008 issue of MSDN Magazine, Mark Beckner introduces ...
Read more!
Separation of presentation and data is not a new idea, but with the growing popularity of technologies such as AJAX and Silver­light, it has become much more prevalent. ADO.NET Data Services Framework began as a way to help developers looking to expose and consume data via services from their applications.. In the August 2008 issue of MSDN ...
Read more!
More ...
資料操作技巧
分層架構中的 Entity Framework
John Papa

本專欄內容是根據 ADO.NET EntityFramework 的搶鮮版所撰寫的。本文包含的所有資訊均有可能變更。

可下載的程式碼位於: DataPoints2008_07.exe (3,549 KB)
在線上瀏覽程式碼
當 N 層架構的架構設計師在評估任何新的技術、模式或策略時,他們必須思考如何讓新的東西與架構緊密結合在一起。只要利用 Entity Framework,整合就不是問題。可以整合到 N 層架構,也可以整合到單層架構。
在本月的專欄中,我要示範如何將 Entity Framework 融入到使用 Windows® Communication Foundation (WCF) 和 Windows Presentation Foundation (WPF) 技術及 Model View Presenter (MVP) 模式的 N 層架構中。我會提出一個範例架構,其中包含邏輯存放區資料庫、資料存取、網域模型、商務管理員等層、服務層、展示層及一個被動 UI 層,我也會示範如何使用 Entity Framework 來整合這幾層。我所使用的所有程式碼範例都可以從 MSDN® Magazine 網站下載。

定義分層
我所提出的應用程式可讓使用者搜尋 NorthwindEF 範例資料庫中的客戶,然後檢視、新增、編輯或刪除客戶。在探究程式碼和範例之前,我們先來討論一下範例的整體架構。因為我要強調的重點不是架構本身,而是如何整合 Entity Framework 與架構設計,所以我選擇一個相當常見的架構,這個架構可以很容易隨著其他策略而加以修改和整合。
[圖 1] 顯示一般分層架構的概觀。上兩層使用 UI 層和展示層來處理使用者介面展示和巡覽。UI 層可以用任何一種技術來實作;但在本專欄及其範例中,我選擇使用 WPF。UI 層採用具有被動檢視的 MVP 模式,這表示檢視 (頂端 UI 層) 是由展示層來管理和控制。展示者負責提供資料給檢視、從檢視中取出要儲存在下層的資料,且通常還會負責處理檢視所引發的事件。
圖 1 架構概觀 (按一下影像以放大圖片)
在我的範例中,展示者會經由 WCF 來與下層通訊。展示者會依據服務的合約來透過 WCF 叫用服務。服務層則會透過服務合約介面來公開其服務。這些合約可讓展示者確知如何呼叫服務。
服務層負責接收來自展示者的通訊,然後呼叫適當的商務層方法,以執行適當的商務邏輯並收集或修改資料。本專案的商務邏輯和 LINQ to Entities 程式碼就是位在商務層。LINQ to Entities 程式碼會參考從 Entity Framework 所產生的實體模型。執行 LINQ 查詢時,Entity Framework 會將 LINQ 查詢轉譯為概念實體模型 (實體資料模型,EDM)、將實體觀點對應到儲存層,然後產生可對資料庫執行的 SQL 查詢。

建置模型
我已經說明架構中各層如何運作的概觀,現在讓我們來探討每一層與 Entity Framework 有關的關鍵環節。因為應用程式已經有資料庫,所以我一開始就從 NorthwindEF 資料庫產生實體模型。
首先,我先建置實體模型,接著也將實體對應到資料庫。我們可以用 EDM 精靈來幫忙產生基底實體模型,然後再依需要來修改模型,以納入繼承、實體分割及其他網域模型概念。[圖 2] 顯示 EDM 精靈,其中已選取要匯入到 EDM 的所有資料表和預存程序。
圖 2 從資料庫產生模型 (按一下影像以放大圖片)
使用 EDM 時經常感到困擾的問題之一,就是 EntitySets 和 EntityTypes 的預設命名慣例。在我的網域模型中,我喜歡使用單數名稱來命名所有的實體。我會建立一個 Customer 的執行個體,或使用 List<Order> 來傳回一份 Order 執行個體清單。每一個實體都是一個藍圖的單數執行個體,藍圖有屬性可定義該實體。
另一方面,我喜歡用複數命名慣例來命名 EntitySets。在要求 ObjectContext 參考其 Customers 或 Orders 的集合時,LINQ 查詢中經常會用到 EntitySets。
我們以下面的 LINQ to Entities 查詢為例來說明這一點:
var q = from c in context.Customers
        select c;
List<Customer> customerList = q.ToList();
這個查詢會告訴 LINQ to Entities 去存取 Customers EntitySet,然後在執行後傳回所有 Customer 實體執行個體。第二行會執行查詢並傳回 List<Customer> 給區域變數 customerList。在本範例中,EntitySet 是複數,所以一看就知道是查詢 EntitySets,然後傳回 Customer (請注意這是單數) 實體的執行個體。
一定要遵照這種命名慣例嗎?當然不是這樣的。不過,我認為這樣可以讓程式碼看起來更清楚。否則,如果您直接使用 EDM 精靈傳回的預設結果,則會得到名稱為 Customers 的 EntitySets 和名稱為 Customers 的 EntityType,這會讓您的 LINQ to Entities 查詢變成這樣:
var q = from c in context.Customers
          select c;
  List<Customers> customerList = q.ToList();
當 EDM 精靈產生模型時,可以很容易修改 EntitySet 和 EntityType 名稱。只要在圖表中選取實體,接著在 [屬性] 視窗中檢視其屬性,然後修改想要的設定即可 (請參閱 [圖 3])。就這個應用程式來說,我藉由設定 Name 屬性來將所有 EntityTypes 修改成單數。我沒有變更 EntitySet Name 屬性,因為它本來就是複數。
圖 3 變更 EntityType 名稱 (按一下影像以放大圖片)

運作方式
現在,我要開始示範應用程式,從檢視 (位於 NWUI 專案中) 和展示者 (位於 NWPresentation 專案中) 開始,由頂層往下來討論其運作方式。這兩個專案都可在本專欄所附的程式碼下載中取得。應用程式會載入客戶搜尋檢視,可讓使用者比對公司名稱準則來搜尋客戶 (請參閱 [圖 4])。檢視是使用 WPF 實作,當使用者與檢視互動時,檢視會引發由展示者所接聽的事件,接著,展示者會採取適當的動作。
圖 4 搜尋客戶 (按一下影像以放大圖片)
當使用者搜尋以字母 D 開頭的所有客戶時,如 [圖 4] 所示,檢視會在使用者按一下 [搜尋 (Search)] 按鈕時引發事件。展示者會接聽這個事件,然後藉由透過 WCF 呼叫服務層來回應,以取得一份要顯示在 CustomerSearchView 上的客戶實體清單。以下是使用者在檢視中按一下 [搜尋 (Search)] 按鈕時的程式碼:
private void btnSearch_Click(object sender,     RoutedEventArgs e)  {
      if (FindCustomerSearchResults != null)          FindCustomerSearchResults();
  }
此程式碼沒有與傳回的實體清單產生互動,而是留給展示者去處理。檢視會使用 WPF 資料繫結來參考實體的屬性,這樣就知道如何將實體清單繫結到清單檢視控制項的項目。檢視與實體之間唯一的互動是透過資料繫結來進行。
CustomerSearchView 會引發事件 FindCustomerSearchResults,而 CustomerSearchPresenter 會接聽事件,然後接手處理並執行搜尋。下列程式碼顯示 CustomerSearchPresenter 類別如何建立 NWServiceClient 類別的執行個體,此類別是下層公開之 WCF 服務的 Proxy:
public void view_FindCustomerSearchResults()
{
    if (this.view.CompanyNameCriteria.Length > 0)
        using (var svc = new NWServiceClient())
        {
            IList<Customer> customerList = svc.FindCustomerList(                view.CompanyNameCriteria);
            view.CustomerSearchResultsList = customerList;
        }
}
NWServiceClient 是從 NWPresentation 專案中使用 ServiceReference 來參考,所以展示者知道如何呼叫服務和將要傳回的資料型別。展示層不會也不應該直接參考 EDM。而是,透過 WCF 所公開的 DataContract 來告知可能的實體型別。這可以讓 Entity Framework 的實體透過 WCF 來跨越實體網路界限而傳遞給展示者。
請注意,當這份 Customer 實體清單傳回之後,在檢視上就會設定為 public 屬性。接著,檢視的這個屬性會接受 List<Customer>,並將其繫結至檢視的 DataContext。展示者會提供資料並傳遞資料,而檢視會處理任何特定的檢視繫結 (因為程式碼完全依技術而定,不論是 WPF、Silverlight®、Windows Form 或 ASP.NET)。
此技巧可讓相同的展示者與實作 ICustomerSearchView 介面的任何檢視一起互動。在這個應用程式中,繫結是以 WPF 繫結技巧來處理,使用的是 DataContext。
合約會公開可由服務層呼叫的方法,以及將傳回的實體。在這個應用程式中,我只有一個傳回 Customer 和 Order 實體型別的方法。這表示合約中只會包含這些實體型別。
WCF 會視情況將 WCF DataContract 屬性套用至實體來處理實體的序列化。藉由透過 DataContract 來公開實體,在 UI 層中不必直接參考 EDM 就可以使用實體。
請注意,從 .NET Framework 3.5 SP1 Beta 1 開始,Entity Framework 會支援自動的圖形序列化。例如,如果父實體有相關聯的子實體,則會序列化父實體及其子實體。在範例應用程式中,因為 OrderManager 的 FindOrderList 方法會使用 LINQ to Entities 查詢,這會立即載入每一張「訂單 (Order)」的「訂單明細 (Order Details)」,所以從中間層傳回的每一個 Order 實體都會包含可透過其巡覽屬性存取的 List<OrderDetail>。
雖然序列化實體可透過 WCF 在展示者與服務層之間傳遞,但 ObjectContext 不會序列化,也不會傳遞給展示者。這表示實體可以在 UI 層使用,但 ObjectContext 會留在下層,這樣才能存取 EDM 和 Entity Framework 的完整資源。
將 ObjectContext 留下表示不能直接在 UI 層中用來擷取或修改實體,也不能在 UI 層中用來管理變更追蹤。無論如何,這些角色最好都留給下層。但是,當實體往下傳回到下層時,應用程式就必須與 ObjectContext 同步處理,這樣才能保存實體中的任何變更。
當使用者按一下 [圖 4] 所示的 [搜尋 (Search)] 按鈕時,展示者會呼叫服務層,再由服務層轉而呼叫商務層 (在 NWBusinessManagers 專案中) 來擷取 List<Customer>。這一層有兩個重要角色。第一個角色是在 EDM 中取得或放置任何資料。第二個角色則是處理任何可能存在的商務邏輯。
CustomerManager 會使用 ObjectContext 來處理與 EDM 的互動,所以就定義一個區域欄位,稱為內容 (context),並於建構函式中建立其執行個體。每一個方法中都可以建立和終結 ObjectContext。不過,最好視需要來開啟和關閉資料庫連接資源。另外,由於可透過類別來存取 ObjectContext,所以不必在類別內傳遞一連串的 private 方法,也就能夠維護變更追蹤。
public CustomerManager()
{
    context = new NWEntities();
}
請注意,就這種應用程式而言,ObjectContext 不應該一直保留不放,而是應該視需要來建立/終結。由於識別解析的緣故,緊抓同一個物件內容不放,最後會導致資料不一致和過時,且在執行識別解析時會造成效能退化 (因為要追蹤的資料愈來愈多),在多執行緒環境中,甚至會導致更新發生問題。
下列程式碼顯示商務層中之 CustomerManager 類別的 FindCustomerList 方法。這個方法會宣告 LINQ to Entities 查詢,此查詢會存取內容來要求以準則開頭的一份 Customer 實體清單。執行這個查詢時,它會評估概念層至儲存層的對應,然後產生適當的 SELECT 命令:
public List<Customer> FindCustomerList(string companyName)
  {
      var q = from c in context.Customers
              where c.CompanyName.StartsWith(companyName)
              select c;
      return q.ToList();
  }
如果喜歡的話,您也可以使用 SQL Server® Profiler 來檢視正在執行的查詢。

保存變更
現在,我已經使用簡單的擷取來逐步解說應用程式,接下來探討如何保存對於資料所做的修改。當使用者編輯客戶時,CustomerView 檢視會顯示繫結至適當的 Customer 實體執行個體 (請參閱 [圖 5])。CustomerView 會向展示者引發事件,然後由展示者轉而從下層要求 Customer 實體執行個體。
圖 5 編輯客戶 (按一下影像以放大圖片)
當使用者修改客戶並加以儲存時,就會利用 [圖 6] 所示的程式碼,將實體從展示者傳遞到下層。此程式碼會評估使用者是新增或修改客戶,然後會呼叫適當的服務層方法,同時傳遞實體。
接著,服務層就將控制權移交給商務層,再由商務層將客戶實體儲存到資料庫。因為客戶實體已經不再是 ObjectContext 的一部分,所以必須先利用 ObjectContext 的 Attach 方法,重新與一個 ObjectContext 結合,如下列程式碼所示。當實體附加至內容之後,實體的屬性必須標示為已修改。做法是利用內容的 ObjectStateManager,並對每一個屬性叫用 SetModified 方法。現在,內容知道實體已被修改,所以可以發出 SaveChanges 方法,這個方法接著會產生 SQL UPDATE 命令,然後對資料庫執行這個命令:
public void UpdateCustomer(Customer customer)
{
    context.Attach(customer);
    customer.SetAllModified(context);     // custom extension method
    context.SaveChanges();
}
請注意,UpdateCustomer 方法中的程式碼會使用我命名為 SetAllModified<T> 的擴充方法,對於要修改的實體,這個方法可以輕鬆地設定所有屬性的狀態。SetAllModified<T> 會根據給定的實體 T 來取得 ObjectStateEntry 的執行個體。然後,它會擷取該實體的所有屬性名稱清單,並對每一個屬性反覆地呼叫 SetModifiedProperty:
public static void SetAllModified<T>(this T entity, ObjectContext  context) 
where T : IEntityWithKey
{
    var stateEntry = context.ObjectStateManager.      GetObjectStateEntry(entity.EntityKey);
    var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.      FieldMetadata.Select
      (pn => pn.FieldType.Name);
    foreach (var propName in propertyNameList)
        stateEntry.SetModifiedProperty(propName);
}
另一種可確實儲存實體的方法是呼叫內容的 Refresh 方法。這會告知內容去取得實體執行個體的資料,並將屬性值更新為資料庫的值。ClientWins 的 RefreshMode 列舉值會將原始值取代為資料庫中最新的值,亦即採用「後進先寫入」的策略。
StoreWins 的 RefreshMode 會將實體快取中的原始值和目前值同時覆寫成資料庫的值。ClientWins 是適合後進先寫入的策略,但如果您想要取消變更,並將 UI 的檢視更新成最新的資料庫值,則 StoreWins 是較適合的策略:
context.Refresh(RefreshMode.ClientWins, customer);  // Last in wins
Entity Framework 在產生 update 和 delete 命令時會強制執行最佳並行處理。做法是對於 ConcurrencyMode 屬性 (Attribute) 值設定為 Fixed 的任何屬性 (Property),一律將原始值放入其 WHERE 子句中。
根據預設,產生的模型不會將任何欄位指定為並行欄位。這表示當使用者儲存變更時,可能不小心就會覆寫另一位使用者所做的變更。如果有另一位使用者在 CustomerView 開啟時變更某個值,而您想要使用最佳並行處理,您可以在概念模型中設定 EntityType 的 ConcurrencyMode 屬性。
編輯 EDM 檔案並將 ConcurrencyMode 設定為 Fixed,就是告訴 Entity Framework 將這個資料行加入至任何 Update 或 Delete 命令的 WHERE 子句。因此,如果找不到相符的資料列,就會引發 OptimisticConcurrencyException。[圖 7] 顯示當我在資料庫中修改客戶的區域時,就在使用者嘗試修改同一個區域之前,就會引發這個例外狀況。
圖 7 OptimisticConcurrencyException (按一下影像以放大圖片)
您可以攔截這個例外狀況,並採取任何適當的動作。例如,您以攔截例外狀況、記錄例外狀況,然後強制覆寫使用者的變更,如下所示:
catch (OptimisticConcurrencyException e){
    context.Refresh(RefreshMode.ClientWins, customer); // Last in wins
    logger.Write(e);
    context.SaveChanges();
}

刪除和新增
當使用者刪除客戶時,CustomerManager 的 DeleteCustomer 方法會取得客戶實體並執行刪除作業:
context.Attach(customer);
context.DeleteObject(customer);
context.SaveChanges();
首先,必須使用 Attach 方法,將 Customer 實體執行個體與 ObjectContext 重新結合。接著,必須從 ObjectContext 中刪除客戶。如此一來,ObjectContext 內的變更追蹤機制就會知道 Customer 實體執行個體已遭刪除。最後,當呼叫 SaveChanges 方法時,ObjectContext 會知道已刪除實體,且應該會產生並執行 DELETE SQL 命令。
當新增客戶時,CustomerManager 的 AddCustomer 方法會取得客戶實體,並執行插入作業,如下所示:
context.AddToCustomers(customer);
context.SaveChanges();
這個實體執行個體是新的,所以必須加入至內容並以旗標標示成 Customer 實體的新執行個體,做法是利用 AddToCustomer 方法,讓 Customer 實體執行個體與 ObjectContext 產生關聯。最後,當呼叫 SaveChanges 方法時,ObjectContext 會知道已新增實體,且應該會產生並執行 INSERT SQL 命令。

結論
我已經示範 Entity Framework 如何整合到架構中、如何使用現代的模式 (例如 MVP 模式),也論及常見的架構問題。Entity Framework 在分層架構中的重點包括:變更追蹤機制、與 LINQ to Entities 的整合、與 ObjectContext 中斷連接和重新連接的能力,並提供開發人員處理並行問題的方法。

如果您要向 John 提出問題或意見,請將郵件寄至 mmdata@microsoft.com

John Papa (johnpapa.net) 是 ASPSOFT (aspsoft.com) 的資深顧問,也是一個棒球迷,在球賽期間都會在家人的陪伴下一起為洋基隊加油。John 是 C# MVP 和 INETA 演講者,發表過許多著作,目前正在寫他最新的一本書,書名是 Data Access with Silverlight 2。他時常在 DevConnections 和 VSLive 等會議上發表演說。


© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.
Page view tracker