Microsoft .NET Pet Shop 4:將 ASP.NET 1.1 應用程式遷移到 2.0 發佈日期: 2006-5-9 | 更新日期: 2006-5-9 適用於:Microsoft .NET Framework 2.0Microsoft Visual Studio 2005Micros ...
Microsoft .NET Pet Shop 4:將 ASP.NET 1.1 應用程式遷移到 2.0
發佈日期: 2006-5-9 | 更新日期: 2006-5-9
適用於:
Microsoft .NET Framework 2.0
Microsoft Visual Studio 2005
Microsoft Windows Server 2003
Microsoft InterNET Information Services
Microsoft Message Queuing
Microsoft SQL Server 2005
Oracle 10G Database
摘要:.NET Pet Shop 應用程式的設計說明瞭構建企業 n 層 .NET 2.0 應用程式的最佳做法,這種應用程式可能需要支持各種資料庫平臺和部署方案。
單擊此處下載 .NET Pet Shop 4.0.msi。
本頁內容
概述 | |
工作效率 | |
從 ASP.NET 1.1 遷移到 2.0 | |
體繫結構 | |
抽象工廠模式 | |
用戶界面增強 | |
加密配置信息 | |
模型對象 | |
Order 和 Inventory 架構 | |
Profile 資料庫架構 | |
小結 |
概述
.NET Pet Shop 應用程式的設計說明瞭構建企業 n 層 .NET 2.0 應用程式的最佳做法,這種應用程式可能需要支持各種資料庫平臺和部署方案。
.NET Pet Shop 4 項目的目標是:
• |
工作效率:減少了 .NET Pet Shop 3 的代碼數量 - 我們減少了近 25% 的代碼。 |
• |
從 ASP.NET 1.1 遷移到 2.0:利用 ASP.NET 2.0 的新功能 - 我們利用母版頁、成員身份和配置文件,並設計出一個新的、吸引人的用戶界面。 圖 1. .NET Pet Shop 4.0 |
• |
企業體繫結構:構建一個靈活的最佳做法應用程式 - 我們實現了設計模式,以及表示層、業務層和數據層的分離。 |
工作效率
與 .NET Pet Shop 3 相比,.NET Pet Shop 4 中的代碼量約減少了近 25%。減少代碼行數的主要好處體現在表示層和數據訪問層。
在表示層,我們減少了大約 25% 的代碼。登錄和簽出步驟比完整的 ASP.NET 頁面更簡潔,需要的代碼和 html 更少。這是因為嚮導控制項本身處理過程流代碼。使用母版頁意味著使用較少的 html 代碼和用戶控制項管理佈局。相比於 Pet Shop 3 用戶管理代碼,成員身份服務處理身份驗證的方式更簡潔。
我們看到數據層節省的代碼量最多,高達 36%。ASP.NET 2.0 SQL 成員身份提供程式取代了帳戶管理代碼。
表 1 給出逐層分解的完整代碼量。
表 1. .NET Pet Shop 版本 3 與版本 4 的代碼量對比 | ||
v3 | v4 | |
表示層 |
1,822 |
1,365 |
模型 |
349 |
395 |
業務邏輯層 |
210 |
199 |
數據訪問層 |
1,538 |
985 |
代碼總行數 |
3,919 |
2,944 |
圖 2 對此做了進一步的圖解。
圖 2. 代碼量對比圖
.NET Pet Shop 4 引入了幾個新功能,包括自定義的 ASP.NET 2.0 配置文件提供程式,以及通過 MSMQ 進行的非同步定單處理等。表 2 顯示新功能的代碼數量:
表 2. .NET Pet Shop 4 新功能的代碼量 | |
自定義配置文件 |
853 |
Oracle 成員身份 |
586 |
緩存依賴項 |
90 |
消息隊列 |
147 |
代碼總行數 |
1,676 |
從 ASP.NET 1.1 遷移到 2.0
為了實現 .NET Pet Shop 4 的目標,我們制定了下列計劃:
• |
使用項目轉換嚮導將 .NET Pet Shop 3.2 代碼基從 ASP.NET 1.1 移植到 ASP.NET 2.0。 |
• |
規劃我們想要包括的 ASP.NET 2.0 功能。 |
• |
實現一個支持這些功能的 n 層體繫結構。 |
項目轉換嚮導
首先,Visual Studio.NET 2005 項目轉換嚮導迅速升級 .NET Pet Shop 3.2 代碼基。通過這一基本移植,我們能夠初步瞭解經過編譯併在 ASP.NET 2.0 上運行的 .NET Pet Shop 3.2。
版本 3 和版本 4 之間的變化
通過升級 .NET Pet Shop 3.2 代碼基以便在.NET Framework 2.0 上運行以及對 ASP.NET 2.0 的研究,我們推出了要在 .NET Pet Shop 4.0 中實現的以下主要功能:
• |
用 System.Transactions 代替服務組件。 |
• |
用強類型集合的泛型代替鬆散類型的 ILists。 |
• |
ASP.NET 2.0 成員身份,用於用戶身份驗證和授權。 |
• |
用於 Oracle 10G 的自定義 ASP.NET 2.0 成員身份提供程式。 |
• |
ASP.NET 2.0 自定義 Oracle 和 SQL Server 配置文件提供程式,用於用戶狀態管理。 |
• |
用母版頁取代 ASP.NET Web 用戶控制項,從而獲得一致的外觀。 |
• |
ASP.NET 2.0 嚮導控制項。 |
• |
使用 SqlCacheDependency(而非基於超時)的資料庫級緩存失效。 |
• |
啟用基於消息隊列構建的非同步 Order 處理。 |
什麼是 System.Transactions?
System.Transactions 是 .NET 2.0 框架中新增的事務控制項命名空間。它是一種處理分散式事務的新方式,沒有 COM+ 註冊和 COM+ 目錄的開銷。請註意,Microsoft 分散式事務協調器用於初始化事務。
運行情況
同步定單處理中的 Order.Insert() 方法使用 System.Transactions 插入一個定單並更新庫存。通過添加對 System.Transaction 命名空間的引用,並將定單插入方法和庫存減少方法包裝在 TransactionScope 內,我們實現了 Order.Insert() 方法,如代碼清單 1 所示。
清單 1. 運行中的 System.Transactions
using System; using System.Transactions; using PetShop.IBLLStrategy; namespace PetShop.BLL { /// <summary> /// This is a synchronous implementation of IOrderStrategy /// By implementing IOrderStrategy interface, the developer can /// add a new order insert strategy without re-compiling the whole /// BLL. /// </summary> public class OrderSynchronous : IOrderStrategy { ... /// <summary> /// Inserts the order and updates the inventory stock within /// a transaction. /// </summary> /// <param name="order">All information about the order</param> public void Insert(PetShop.Model.OrderInfo order) { using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required)) { dal.Insert(order); // Update the inventory to reflect the current inventory // after the order submission. Inventory inventory = new Inventory(); inventory.TakeStock(order.LineItems); // Calling Complete commits the transaction. // Excluding this call by the end of TransactionScope's // scope will rollback the transaction. ts.Complete(); } } } }
在 .NET Pet Shop 3 中,分散式事務由企業服務處理,需要 COM+ 註冊。OrderInsert 類從服務組件派生,事務由 COM+ 處理。然後,服務組件使用 regsvr32 命令進行註冊。
清單 2. Pet Shop 3 的定單插入
using System; using System.Collections; using System.EnterpriseServices; using System.Runtime.InteropServices; ... namespace PetShop.BLL { /// <summary> /// A business component to manage the creation of orders /// Creation of an order requires a distributed transaction /// so the Order class derives from ServicedComponents /// </summary> [Transaction(System.EnterpriseServices.TransactionOption.Required)] [ClassInterface(ClassInterfaceType.AutoDispatch)] [ObjectPooling(MinPoolSize=4, MaxPoolSize=4)] [Guid("14E3573D-78C8-4220-9649-BA490DB7B78D")] public class OrderInsert : ServicedComponent { ... /// <summary> /// A method to insert a new order into the system /// The orderId will be generated within the method and should not /// be supplied as part of the order creation the inventory will be /// reduced by the quantity ordered. /// </summary> /// <param name="order">All the information about the order</param> /// <returns> /// The new orderId is returned in the order object /// </returns> [AutoComplete] public int Insert(OrderInfo order) { // Get an instance of the Order DAL using the DALFactory IOrder dal = PetShop.DALFactory.Order.Create(); // Call the insert method in the DAL to insert the header int orderId = dal.Insert(order); // Get an instance of the Inventory business component Inventory inventory = new Inventory(); inventory.TakeStock( order.LineItems); ... // Set the orderId so that it can be returned to the caller return orderId; } } }
System.Transactions 的優點
從企業服務移動到 System.Transactions 可以簡化部署,因為後者不需要使用 COM+ 目錄。使用 COM+ 目錄時,我們忽略了其他一些額外的功能,只保留了分散式事務支持。System.Transaction 使得在 ASP.NET 2.0 應用程式中編程和部署分散式應用程式變得十分簡單。System.Transactions 在運行時的性能提高了 50%,因為它避免了對象實例化的 COM+ 目錄查找所產生的開銷。最後一個優點是,針對 SQL Server 2005 運行時,System.Transactions 能夠檢測到某個分散式事務何時針對宿主在一個 SQL Server 2005 實例上的兩個不同資料庫運行。在這種情況下,它能夠將該分散式事務提升為一個本地事務,這樣就可避免與分散式事務登錄/兩階段提交相關的全部開銷,從而極大地提高性能。
泛型
什麼是泛型?
每次返回一個 Pet Shop 模型對象集合時,我們都針對該對象使用泛型類型的一個集合列表。這是 C# 2.0 的一個新增功能,稱之為泛型。
運行情況
我們可以從清單 3 所示的 GetProductsByCategory 方法中看到泛型的運行情況。
清單 3. Product.cs (Pet Shop 4.0)
/// <summary> /// A method to retrieve products by category name /// </summary> /// <param name="order">The category name to search by</param> /// <returns>A Generic List of ProductInfo</returns> public IList<ProductInfo>GetProductsByCategory(string category) { // Return new if the string is empty if (string.IsNullOrEmpty(category)) return new List<ProductInfo>(); // Run a search against the data store return dal.GetProductsByCategory(category); }
以下是 Pet Shop 3 中的等效代碼,它返回一個 IList:
清單 4. Product.cs (Pet Shop 3)
/// <summary> /// A method to retrieve products by category name /// </summary> /// <param name="order">The category name to search by</param> /// <returns> /// An interface to an arraylist of the search results /// </returns> public IList GetProductsByCategory(string category) { // Return null if the string is empty if (category.Trim() == string.Empty) return null; // Get an instance of the Product DAL using the DALFactory IProduct dal = PetShop.DALFactory.Product.Create(); // Run a search against the data store return dal.GetProductsByCategory(category); }
泛型的優點
泛型允許我們返回對象的強類型集合,而不是 .NET Pet Shop 3 中的 IList 集合。泛型強類型集合提供類型安全,其性能優於普通的集合。另外,泛型強類型集合將在 Visual Studio 2005 Intellisense 中出現,這可以提高開發人員工作效率。
ASP.NET 2.0 成員身份
成員身份提供一個通用的用戶身份驗證和管理框架。當用戶信息存儲在 SQL Server 中時,.NET Pet Shop 4 使用 SQL Server 成員身份提供程式;當用戶信息存儲在 Oracle 中時,.NET Pet Shop 4 使用自定義成員身份提供程式。
運行情況
要在 .NET Pet Shop 4 中實現成員身份,需要執行以下步驟:
• |
配置窗體身份驗證。 <authentication mode="Forms"> <forms name="PetShopAuth" loginUrl="SignIn.aspx" protection="None" timeout="60"/> </authentication> |
• |
要使用 SQL 成員身份提供程式,我們必須安裝成員身份資料庫。成員身份資料庫是在運行下列命令時由 ASP.NET 創建的。 %WinDir%\Microsoft.NET\Framework\<.NET version>\aspnet_regsql -S <server\instance> -E -A all -d MSPetShop4Services |
• |
配置 SQL 成員身份提供程式。 <membership defaultProvider="SQLMembershipProvider"> <providers> <add name="SQLMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SQLMembershipConnString" applicationName=".NET Pet Shop 4.0" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed"/> </providers> </membership> |
• |
ASP.NET Login 控制項封裝所有登錄邏輯。CreateUserWizard 控制項處理新的用戶註冊。 |
ASP.NET 2.0 成員身份的優點
通過成員身份服務,我們能夠使用預建的用戶身份驗證和註冊控制項,而無需從頭編寫這些控制項。最終結果是:為登錄、登錄狀態、用戶身份、用戶註冊和密碼恢復編寫的代碼變少了。
而且,由於成員身份現在駐留在自己的資料庫中,因此我們能夠刪除 .NET Pet Shop 3 中使用的 Accounts 表,然後使用 ASP.NET 2.0 創建的成員身份服務資料庫。
用於 Oracle 10G的自定義成員身份提供程式
.NET 2.0 框架包括一個 SQL Server 成員身份提供程式。為了在應用程式使用 Oracle 成員身份資料庫時保留用戶帳戶,我們為 Oracle 創建了一個自定義成員身份提供程式實現。我們僅實現了由 .NET Pet Shop 4 使用的方法,即 CreateUser 方法和 Login 方法。然而,任何希望將 Oracle10G與 ASP.NET 成員身份服務一起使用的用戶都可以使用和/或擴展該代碼。
運行情況
CreateUser 方法是 MembershipProvider 類的實現的方法之一。它深入探究 OracleMembershipProvider 的工作方式。
清單 5. OracleMembershipProvider.cs CreateUser(...)
using System; using System.Configuration.Provider; namespace PetShop.Membership { class OracleMembershipProvider : MembershipProvider { string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object userId, out MembershipCreateStatus status) { // create connection OracleConnection connection = new OracleConnection(OracleHelper.ConnectionStringMembership); connection.Open(); OracleTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); try { DateTime dt = DateTime.Now; bool isUserNew = true; // Step 1: Check if the user exists in the Users // table: Create if not int uid = GetUserID(transaction, applicationId, username, true, false, dt, out isUserNew); if (uid == 0) { // User not created successfully! status = MembershipCreateStatus.ProviderError; return null; } // Step 2: Check if the user exists in the Membership table: Error // if yes if (IsUserInMembership(transaction, uid)) { status = MembershipCreateStatus.DuplicateUserName; return null; } // Step 3: Check if Email is duplicate if (IsEmailInMembership(transaction, email, applicationId)) { status = MembershipCreateStatus.DuplicateEmail; return null; } // Step 4: Create user in Membership table int pFormat = (int)passwordFormat; if (!InsertUser(transaction, uid, email, pass, pFormat, salt, "", "", isApproved, dt)) { status = MembershipCreateStatus.ProviderError; return null; } // Step 5: Update activity date if user is not new if(!isUserNew) { if(!UpdateLastActivityDate(transaction, uid, dt)) { status = MembershipCreateStatus.ProviderError; return null; } } status = MembershipCreateStatus.Success; return new MembershipUser(this.Name, username, uid, email, passwordQuestion, null, isApproved, false, dt, dt, dt, dt, DateTime.MinValue); } catch(Exception) { if(status == MembershipCreateStatus.Success) status = MembershipCreateStatus.ProviderError; throw; } finally { if(status == MembershipCreateStatus.Success) transaction.Commit(); else transaction.Rollback(); connection.Close(); connection.Dispose(); } }
未實現的方法成為空存根,如下所示:
public override string GetUserNameByEmail(string email) { throw new Exception("The method or operation is not implemented."); }
Oracle 10G成員身份提供程式的優點
由於我們希望 .NET Pet Shop 4 將成員身份數據存儲在 Oracle 資料庫以及 SQL Server 中,因此我們實現了一個自定義的成員身份提供程式。提供程式模型使我們能夠將 Oracle 資料庫與 ASP.NET 2.0 成員身份服務進行簡單、快速地集成。
ASP.NET 2.0 配置文件
在 ASP.NET 2.0 中,可跨越多個 Web 應用程式,將用戶信息存儲到一個名為 Profile 的新服務中。.NET Pet Shop 4 的配置文件實現存儲並檢索用戶的購物車、購物清單和帳戶信息。這裡的關鍵的一點是,很多用戶會發現,假如為用戶會話信息提供一個事務處理、集群安全的存儲,這幾乎可以完全替換他們對會話對象的使用。預設情況下,配置文件服務將數據序列化為一個 BLOB,存儲在資料庫中。但是,通過實現您自己的配置文件服務序列化服務可以獲得更高的性能。對於 Pet Shop 4,創建了一個配置文件服務的自定義實現來降低序列化開銷。
運行情況
• |
配置配置文件提供程式。 清單 6. 配置文件提供程式配置 <profile automaticSaveEnabled="false" defaultProvider="ShoppingCartProvider"> <providers> <add name="ShoppingCartProvider" connectionStringName="SQLProfileConnString" type="PetShop.Profile.PetShopProfileProvider" applicationName=".NET Pet Shop 4.0"/> <add name="WishListProvider" connectionStringName="SQLProfileConnString" type="PetShop.Profile.PetShopProfileProvider" applicationName=".NET Pet Shop 4.0"/> <add name="AccountInfoProvider" connectionStringName="SQLProfileConnString" type="PetShop.Profile.PetShopProfileProvider" applicationName=".NET Pet Shop 4.0"/> </providers> <properties> <add name="ShoppingCart" type="PetShop.BLL.Cart" allowAnonymous="true" provider="ShoppingCartProvider"/> <add name="WishList" type="PetShop.BLL.Cart" allowAnonymous="true" provider="WishListProvider"/> <add name="AccountInfo" type="PetShop.Model.AddressInfo" allowAnonymous="false" provider="AccountInfoProvider"/> </properties> </profile> |
• |
遷移匿名配置文件。 清單 7. 遷移匿名配置文件 // Carry over profile property values from an anonymous to an // authenticated state void Profile_MigrateAnonymous(Object sender, ProfileMigrateEventArgs e) { ProfileCommon anonProfile = Profile.GetProfile(e.AnonymousID); // Merge anonymous shopping cart items to the authenticated // shopping cart items foreach (CartItemInfo cartItem in anonProfile.ShoppingCart.CartItems) Profile.ShoppingCart.Add(cartItem); // Merge anonymous wishlist items to the authenticated wishlist // items foreach (CartItemInfo cartItem in anonProfile.WishList.CartItems) Profile.WishList.Add(cartItem); // Clean up anonymous profile ProfileManager.DeleteProfile(e.AnonymousID); AnonymousIdentificationModule.ClearAnonymousIdentifier(); // Save profile Profile.Save(); } 清單 8. 購物車 using System; using System.Collections.Generic; using PetShop.Model; namespace PetShop.BLL { /// <summary> /// An object to represent a customer's shopping cart. /// This class is also used to keep track of customer's wish list. /// </summary> [Serializable] public class Cart { // Internal storage for a cart private Dictionary cartItems = new Dictionary(); /// <summary> /// Calculate the total for all the cartItems in the Cart /// </summary> public decimal Total { get { decimal total = 0; foreach (CartItemInfo item in cartItems.Values) total += item.Price * item.Quantity; return total; } } /// <summary> /// Update the quantity for item that exists in the cart /// </summary> /// Item Id /// Quantity public void SetQuantity(string itemId, int qty) { cartItems[itemId].Quantity = qty; } /// <summary> /// Return the number of unique items in cart /// </summary> public int Count { get { return cartItems.Count; } } /// <summary> /// Add an item to the cart. /// When ItemId to be added has already existed, this method /// will update the quantity instead. /// </summary> /// Item Id of item to add public void Add(string itemId) { CartItemInfo cartItem; if (!cartItems.TryGetValue(itemId, out cartItem)) { Item item = new Item(); ItemInfo data = item.GetItem(itemId); if (data != null) { CartItemInfo newItem = new CartItemInfo(itemId, data.ProductName, 1, (decimal)data.Price, data.Name, data.CategoryId, data.ProductId); cartItems.Add(itemId, newItem); } } else cartItem.Quantity++; } /// <summary> /// Add an item to the cart. /// When ItemId to be added has already existed, this method /// will update the quantity instead. /// </summary> /// Item to add public void Add(CartItemInfo item) { CartItemInfo cartItem; if (!cartItems.TryGetValue(item.ItemId, out cartItem)) cartItems.Add(item.ItemId, item); else cartItem.Quantity += item.Quantity; } /// <summary> /// Remove item from the cart based on itemId /// </summary> /// ItemId of item to remove public void Remove(string itemId) { cartItems.Remove(itemId); } /// <summary> /// Returns all items in the cart. Useful for looping through /// the cart. /// </summary> /// Collection of CartItemInfo public ICollection CartItems { get { return cartItems.Values; } } /// <summary> /// Method to convert all cart items to order line items /// </summary> /// A new array of order line items public LineItemInfo[] GetOrderLineItems() { LineItemInfo[] orderLineItems = new LineItemInfo[cartItems.Count]; int lineNum = 0; foreach (CartItemInfo item in cartItems.Values) orderLineItems[lineNum] = new LineItemInfo(item.ItemId, item.Name, ++lineNum, item.Quantity, item.Price); return orderLineItems; } /// <summary> /// Clear the cart /// </summary> public void Clear() { cartItems.Clear(); } } } |
ASP.NET 2.0 配置文件的優點
使用 ASP.NET 2.0,用戶的購物車可以存儲在資料庫中並持久保留,這樣,如果用戶兩三天後再回來,他們仍然擁自己的購物車。此外,配置文件服務是"按需"提供的,而會話狀態對象對於任何引用它的頁面,每頁都要進行重新載入;配置文件服務的一個優勢是只在實際需要時才載入。
而且,使用配置文件功能,我們能夠從現有的 Pet Shop 3 資料庫中刪除 Account 表和 Profile 表,這樣也會減少業務邏輯層和數據訪問層中的代碼量。
母版頁
ASP.NET 2.0 提供一種通過使用母版頁保持整個 Web 站點外觀一致的新技術。.NET Pet Shop 4 母版頁包含標頭、LoginView 控制項、導航菜單和呈現內容的 HTML。所有其他 Pet Shop Web 窗體都使用 Pet Shop 4 母版頁。
運行情況
圖 3 展示 .NET Pet Shop 4 母版頁。
圖 3. .NET Pet Shop 4 母版頁
清單 9. 綁定母版頁
<%@ Page AutoEventWireup="true" Language="C#" MasterPageFile="~/MasterPage.master" Title="Products" Inherits="PetShop.Web.Products" CodeFile="~/Products.aspx.cs" %>
ASP.NET 2.0 母版頁的優點
使用母版頁,我們能夠只創建一種佈局,然後即可對所有 .NET Pet Shop 頁重用該佈局。開發期間對該佈局的任何更改都直接作用於母版頁,其他頁面更改的只涉及到內容。相反,通過將標頭和導航欄封裝在名為 NavBar.ascx 的 ASP.NET 用戶控制項中,可以實現 .NET Pet Shop 3 中的用戶界面。.NET Pet Shop 3 中的每個 Web 窗體都包含用於控制佈局的 NavBar 用戶控制項和 HTML。更改佈局將涉及處理每個 Web 窗體上的 NavBar 用戶控制項,或修改每個 Web 窗體上的 HTML。
ASP.NET 2.0 嚮導控制項
.NET Pet Shop 4 中的簽出過程包含在 CheckOut 頁面上的一個 Wizard 控制項中。Wizard 是一個新控制項,它提供一種實現逐步過程的新方法。Wizard 控制項管理窗體間的導航、數據持久性以及每一步的狀態管理。
運行情況
圖 4. 簽出嚮導控制項
ASP.NET 2.0 嚮導控制項的優點(單擊圖像查看大圖像)
.NET Pet Shop 3 中的簽出過程涉及一系列互相通信的 ASP.NET 頁面。從購物車頁面,用戶可以轉到簽出頁面;在簽出頁面,用戶輸入其賬單信息,最後系統處理定單。該流程由一個名為 CartController 的自定義類控制,它使用會話狀態管理步驟間的通信。
圖 5. .NET Pet Shop 3 簽出過程
使用 Wizard 控制項,只需要較少的代碼即可在 .NET Pet Shop 4 中實現簽出,從而使該過程變得十分簡單。
資料庫級緩存失效
SQL Cache Dependency 是 ASP.NET 2.0 的一個新增對象,可用於在 SQL Server 中的數據更改時使緩存失效。Pet Shop 4 使用 SQL Cache Dependency 對象使目錄、產品和項目緩存失效。
就現成可用的功能而言,Pet Shop 僅包含基於表的緩存依賴項實現。開發人員可以通過擴展 CacheDependency 對象來實現自己的緩存失效機制。機制實現後,就可以從 Web.config 配置 Pet Shop 4 的 CacheDependency。
請註意,Pet Shop 4 的 SQL CacheDependency 僅僅是為在 SQL Server 上運行而設計的。對於 Oracle,.NET Pet Shop 4 將回到基於時間的緩存過期。
運行情況
圖 6 顯示 SQL Server 的緩存依賴項:
圖 6. Pet Shop 表緩存依賴項
資料庫級緩存失效的優點
使用緩存失效,我們可以使顯示的內容與 Pet Shop 資料庫中的數據保持一致,但是仍能實現中間層對象緩存的優勢,從而降低中間層上的運行時處理要求,以及減少資料庫調用。這樣可以提高應用程式的可伸縮性(它可以處理更多併發用戶),同時還可降低資料庫負載。
非同步 Order 處理
我們作的另一處更改是添加了一個選項,以配置定單過程應該將事務直接(同步)提交給資料庫,還是應該提交給指定的隊列,稍後再對該隊列中的定單進行處理(非同步)。在非同步定單處理過程中,如果用戶提交了一個定單,該定單將進入一個隊列。.NET Pet Shop 4 有一個要存儲在 Microsoft 消息處理隊列 (MSMQ) 中的實現。稍後,該定單隊列可以由 Order 處理器控制台應用程式處理。該方法的一個優勢是,定單資料庫甚至不必進行客戶運行,就能夠處理定單。由於 MSMQ 使用持久隊列,因此無需用戶干涉仍然能夠捕獲所有定單,一旦處理應用程式和定單資料庫再次上線,所有定單都將插入資料庫中。
運行情況
為了處理同步和非同步定單處理之間的演算法改變,我們使用策略模式。在策略模式中,發出定單的方法是從 BLL.Order.Insert 方法中分離出來的。根據 OrderStrategy 的 Web.config 設置,使用了對應的 Insert 方法。預設情況下,.NET Pet Shop 配置為同步運行。
要配置 Order 策略,將 OrderStrategyClass 值從 OrderSynchronous 更改為 OrderAsynchronous。此外,對於非同步定單處理,MSMQ 必須通過一個為 Pet Shop 而創建的私有隊列來啟用,如下所示。
<add key="OrderStrategyClass" value="PetShop.BLL.OrderSynchronous"/> <add key="OrderQueuePath" value="private queue path"/>
同步發出定單
圖 7 說明同步發出定單。當用戶簽出定單時,簽出按鈕單擊事件處理程式調用 BLL 中的 Order Insert 方法。對於同步發出定單,BLL Order 對象使用OrderSynchronousInsert 方法將新定單插入 Orders 資料庫中,然後更新 Inventory 資料庫以反映定單提交後的當前庫存。
圖 7. 同步發出定單
非同步發出定單
圖 8 說明非同步發出定單。在 Web 站點上,如果用戶單擊 CheckOut 按鈕,就會調用 BLL Order Insert 方法。然而,由於 OrderStrategy 是針對非同步配置的,所以使用 OrderAsynchronous 策略。OrderAsynchronous 插入方法直接將定單信息發送到隊列中。
圖 8. 非同步發出
定單處理器
定單處理器是一個控制台應用程式,我們創建它的目的是接收消息處理實現中的定單,並將這些定單轉錄到 Order 資料庫和 Inventory 資料庫中。定單處理器以多線程方式運行,以批處理方式處理定單。它重用同步定單策略將新定單插入到 Orders 資料庫中,並減少 Inventory 資料庫中的值。
非同步定單處理的優點
其他許多企業應用程式也使用了非同步定單處理。要想使 .NET Pet Shop 4 在定單以多線程方式處理時性能更佳,分離定單過程不失為一種方法。
返回頁首體繫結構
對於早期版本的 .NET Pet Shop,體繫結構重點關註用戶界面、應用程式邏輯和數據之間的完全分離。這一完全分離允許我們更改一個層的實現,而不會影響其他層。例如,我們可以更改資料庫供應商,而不必更改業務邏輯代碼。
圖 9 中的圖表說明 .NET Pet Shop 4 的高級邏輯體繫結構。表示層 (WEB) 包含各種用戶界面元素。業務邏輯層 (BLL) 包含應用程式邏輯和業務組件。數據訪問層 (DAL) 負責與資料庫交互,進行數據存儲和檢索。以下各部分中將對各層進行詳細討論。
圖 9. .NET Pet Shop 4 的體繫結構圖(單擊圖像查看大圖像)
返回頁首抽象工廠模式
.NET Pet Shop 4 使用抽象工廠設計模式,該模式中的介面用於創建一系列相關或依賴的對象,而無需指定其具體類。數據訪問層中有一個該模式的示例,其中包括針對 IDAL、DAL 工廠、Oracle DAL 和 SQL Server DAL 的項目。為緩存、庫存和定單數據訪問、消息處理,以及配置文件數據訪問創建抽象工廠。
表示層
ASP.NET 2.0 包括許多可以提高開發人員工作效率的內置功能。構建 .NET Pet Shop 4 時,我們重新設計了用戶界面,從而可以利用 ASP.NET 2.0 提供的新功能,如母版頁、主題、皮膚、Wizard 控制項和 Login 控制項。為了保留用戶帳戶,我們利用成員身份提供程式(而不是使用 ASP.NET 會話狀態)存儲用戶的購物車和喜歡的產品。新的配置文件提供程式可以存儲可使編程和管理用戶狀態更加簡單的強類型購物車。使用所有這些功能,我們能夠快速實現 Pet Shop 表示層更改。
返回頁首用戶界面增強
.NET Pet Shop 4 徹底地呈現出一種新外觀。新的用戶界面支持更大的寵物目錄,使用戶可以更容易地查找和購買各種新寵物。更改 .NET Pet Shop 用戶界面的外觀後,我們對 .NET Pet Shop 提供的示例寵物很感興趣。.NET Pet Shop 中現在有企鵝、小蟲、熊貓,甚至骨骼、恐龍和透明的小貓!通過添加購物清單、瀏覽途徑記錄以及其他小功能,我們還可以改善購物體驗。
返回頁首加密配置信息
.NET Framework 2.0 引入了一個受保護的配置功能,我們可以使用該功能加密連接字元串。使用該功能,我們可以加密敏感的資料庫用戶名和密碼信息。
當您選擇"full source and database install"選項時,.NET Pet Shop 安裝程式將自動運行一段腳本,以加密 Web.config 文件中存儲的連接字元串。
要在"source only"安裝上執行配置加密,運行安裝目錄中的 EncryptWebConfig.bat 文件。
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ASPNET_regiis.exe -pef "connectionStrings" "C:\Program Files\Microsoft\.NET Pet Shop 4.0\Web"
業務邏輯層
.NET Pet Shop 4 的業務邏輯保留了 .NET Pet Shop 3 的大部分業務邏輯,如 Model 對象及其使用方式。為數不多的更改包括使用泛型,非同步發出定單,以及System.Transactions 命名空間。
返回頁首模型對象
.NET Pet Shop 4 保留了 .NET Pet Shop 3 中的 Model 對象。這些對象是模仿資料庫表結構的自定義輕量級類。它們在各應用程式層之間共用以相互通信。例如,如果返回一個目錄中的多個產品,我們就返回一個 Product Model 對象集合。
數據訪問層
BLL 與數據訪問層通信以訪問 Pet Shop 4 資料庫中的數據。.NET Pet Shop 4 使用以下四個資料庫:Inventory、Orders、Membership 和 Profile。對於 .NET Pet Shop 3,該版本支持 Oracle 和 SQL Server 資料庫。
返回頁首Order 和 Inventory 架構
.NET Pet Shop 4 中使用的 Orders 和 Inventory 的資料庫架構是從 .NET Pet Shop 3 移植而來的。刪除了幾個未使用的欄位。該資料庫具有以下表的總體結構:
圖 10. Pet Shop Orders 資料庫
圖 11. Pet Shop Inventory 資料庫
返回頁首Profile 資料庫架構
Profile 資料庫用於存儲特定於用戶的信息,如帳戶信息和購物車內容。該資料庫具有以下表的總體結構:
圖 12. Pet Shop Profile 資料庫
返回頁首小結
Microsoft .NET Pet Shop 4.0 應用程式用來重點說明構建可伸縮企業 Web 應用程式所使用的關鍵技術和體繫結構。由於 ASP.NET 2.0 中的增強功能,我們能夠更快速地構建 n 層企業應用程式,這使我們可以花時間構建更豐富、功能更全面的 Pet Shop。
.NET 2.0 的主要更改和新增功能包括:
• |
System.Transactions:允許更快速的事務處理和更簡單的部署,而無需使用 COM+ 目錄。 |
• |
泛型:允許我們返回對象的強類型集合而不是 .NET Pet Shop 3 中的 IList 集合。支持更簡單的編碼,因為智能感知會識別集合中的類型對象。 |
• |
ASP.NET 2.0 成員身份服務:提供了通用的用戶身份驗證和管理框架,可以極大地減少與創建和維護用戶帳戶信息相關的代碼量。允許我們使用預建的用戶身份驗證和註冊控制項,而無需從頭編寫這些控制項。最終結果是可以為登錄、登錄狀態、用戶身份、用戶註冊和密碼恢復編寫更少的代碼。 |
• |
ASP.NET 2.0 配置文件服務:為特定於用戶的信息(如購物車)替換會話對象的使用。為用戶會話信息提供了一個集群安全的事務處理存儲,用戶會話信息可以跨多個用戶對站點的訪問進行維護。 |
• |
ASP.NET 2.0 母版頁:提供了一種新技術以保持整個 Web 站點的外觀一致,僅通過更新母版頁,就可以輕鬆地將全局更改應用於站點多個頁面的外觀。 |
• |
ASP.NET 2.0 嚮導控制項:一個新的伺服器端控制項,它提供了一種實現逐步過程的簡單方法。我們使用它來減少 Pet Shop 4.0 簽出過程的編碼量。 |
• |
ASP.NET 2.0 是 SQL 緩存依賴項:允許中間層對象緩存在後端資料庫信息更改時自動失效。對於 SQL 2000,它在表級別有效(如同在 Pet Shop 4 中一樣),而對於 SQL Server 2005,它也可以在單獨的行級別有效。利用該功能,緩存的資料庫信息可以始終保持是最新的,同時仍利用緩存來降低中間層和資料庫伺服器上的負載。 |
• |
通過消息處理的非同步定處理選項:雖然不是 .NET 2.0 的功能(.NET 1.1 也提供此功能),我們還是對 Pet Shop 4 進行了擴展,使其能夠選擇是通過 MSMQ 和System.Messaging 使用消息處理,還是直接將標準的同步事務用於資料庫。倘若有更強的可靠性和潛在的可伸縮性,這會將定單處理與定單資料庫分離。 |