適用於:
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 的新功能 - 我們利用母版頁、成員身份和配置文件,並設計出一個新的、吸引人的用戶界面。
企業體系結構:構建一個靈活的最佳做法應用程序 - 我們實現了設計模式,以及表示層、業務層和數據層的分離。
與 .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 對此做了進一步的圖解。
.NET Pet Shop 4 引入了幾個新功能,包括自定義的 ASP.Net 2.0 配置文件提供程序,以及通過 MSMQ 進行的異步定單處理等。表 2 顯示新功能的代碼數量:
表 2. .Net Pet Shop 4 新功能的代碼量自定義配置文件
853
Oracle 成員身份
586
緩存依賴項
90
消息隊列
147
代碼總行數
1,676
為了實現 .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_r
配置 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 方法。然而,任何希望將 Oracle 10G 與 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"
</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 {
// Internalstorage 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.
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 控件管理窗體間的導航、數據持久性以及每一步的狀態管理。
運行情況
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 對象使用 OrderSynchronous Insert 方法將新定單插入 Orders 數據庫中,然後更新 Inventory 數據庫以反映定單提交後的當前庫存。
圖 7. 同步發出定單
異步發出定單
圖 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\AS PNET_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 數據庫。
返回頁首
.NET Pet Shop 4 中使用的 Orders 和 Inventory 的數據庫架構是從 .Net Pet Shop 3 移植而來的。刪除了幾個未使用的字段。該數據庫具有以下表的總體結構:
圖 10. Pet Shop Orders 數據庫
圖 11. Pet Shop Inventory 數據庫
返回頁首
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 使用消息處理,還是直接將標准的同步事務用於數據庫。倘若有更強的可靠性和潛在的可伸縮性,這會將定單處理與定單數據庫分離。
轉到原英文頁面
返回頁首