概述
將一個ASP.NET網站分離為多個獨立的模組,一個最大的問題就是與頁面相關聯的大多數商務邏輯駐留在該頁面的原始碼檔案中,我們幾乎做不到將原始碼檔案分為多個獨立的程式集。為了真正建立獨立的與網站中的頁面相關聯的功能模組,所有頁面邏輯、事件處理邏輯和導航邏輯需要以某種方式從頁面提取出來並儲存在獨立的程式集中。
Web Composite應用程式塊中的預設解決方案是使用 View-Presenter 模式將頁面邏輯分成不同的用於響應由視圖(網頁)轉寄的任意事件的類(表示器)。表示器類完全在業務模組中實現,從而將應用程式邏輯至於網站外,介面是在定義由視圖實現的方法的業務模組中定義的。這樣,網頁可在結束時將所有事件轉寄給表示器,無需任何實際的特定於應用程式的職責。這樣還可使設計表示器的測試更為容易,無需實際涉及到前端網頁。
添加View
還是接著我們在上一篇中的樣本,已經建立了相關的業務模組和服務,這裡添加一個視圖:
這裡我們建立一個顯示商品詳細資料的視圖ProductDetail,建立完成後,在資源管理員中可以看到:
添加了新頁面ProductDetail.aspx 以及相應的原始碼檔案添加到 /Products 目錄。對於Products 業務模組項目,該方案將添加新的類 ProductDetailPresenter (表示器)和相應的 IProductDetail (視圖)介面,該介面已經由 ProductDetail.aspx檔案中的原始碼類實現,同時原始碼類檔案還包含一個頁面應向其轉寄事件的相應的 ProductDetailPresenter 類的屬性聲明:
public partial class Products_ProductDetail : System.Web.UI.Page, IProductDetail{private ProductDetailPresenter _presenter;protected void Page_Load(object sender, EventArgs e){if (!this.IsPostBack){this._presenter.OnViewInitialized();}this._presenter.OnViewLoaded();}[CreateNew]public ProductDetailPresenter Presenter{set{this._presenter = value;this._presenter.View = this;}}}
由於我們要顯示Product的名稱和品牌,開啟IProductDetail.cs檔案,添加如下兩個屬性:
public interface IProductDetail{public string Name;public string Brand;}
然後我們在頁面中實現這兩個屬性:
public partial class Products_ProductDetail : System.Web.UI.Page, IProductDetail{private ProductDetailPresenter _presenter;protected void Page_Load(object sender, EventArgs e){if (!this.IsPostBack){this._presenter.OnViewInitialized();}this._presenter.OnViewLoaded();}[CreateNew]public ProductDetailPresenter Presenter{set{this._presenter = value;this._presenter.View = this;}}public string Name{set{this.lbl_Name.Text = value;}}public string Brand{set{this.lbl_Brand.Text = value;}}}
實現Presenter
從上面的代碼中可以看到,頁面的原始碼檔案中非常乾淨,沒有任何與商務邏輯有關的東西,至於資料從哪兒來,該怎麼進行顯示,就交給Presenter。接下來就要實現Presenter。開啟ProductDetailPresenter.cs檔案,這裡可以實現頁面中的任何事件,其中OnViewLoaded、OnViewInitialized兩個方法的區別在於對應我們在Page中的IsPostBack判斷,View屬性是定義在泛型的Presenter中。編寫完成後代碼如下:
public class ProductDetailPresenter : Presenter<IProductDetail>{private ProductsController _controller;public ProductDetailPresenter([CreateNew] ProductsController controller){_controller = controller;}public override void OnViewLoaded(){Product product = _controller.GetProductById("1");View.Name = product.Name;View.Brand = product.Brand;}public override void OnViewInitialized(){}}
在瀏覽器中查看後,可以看到頁面如下:
這裡有個問題是頁面的顯示出來了,但是在左邊的樹形控制項導航中並沒有看到ProcuctDetail的連結。這是下面要說的模組網站映射。
模組網站映射
在解決方案主版頁面面中,包含一個綁定到 SiteMapDataSource 的樹狀檢視控制項,用於顯示網站上的可導航頁面,另外還有SiteMapPath控制項。這裡的SiteMapDataSource並沒有綁定到標準 Web.sitemap 檔案,而是綁定到獨立地從每個模組收集導航資訊的自訂SiteMapProvider。該Provider在應用程式啟動時顯式詢問每個模組的網站地圖資訊,預設情況下,此 SiteMapProvider 作為預設提供者註冊,並將由 SiteMapDataSource 用於所有導航控制項。
要使用特定模組的網站地圖資訊填充 ModuleSiteMapProvider,需要覆寫從 ModuleInitializer 派生的類中的 RegisterSiteMapInformation 方法。我們在建立業務模組時會覆寫此方法,並會將業務模組下的Default.aspx 頁面插入到 SiteMapNodes 集合,但是我們需要將其他任何頁面添加到網站的同時也將其添加到集合中。
protected virtual void RegisterSiteMapInformation(ISiteMapBuilderService siteMapBuilderService){SiteMapNodeInfo moduleNode = new SiteMapNodeInfo("Products", "~/Products/Default.aspx", "Products");siteMapBuilderService.AddNode(moduleNode);SiteMapNodeInfo productDetailNode = new SiteMapNodeInfo("ProductDetail","~/Products/ProductDetail.aspx","ProductDetail");siteMapBuilderService.AddNode(productDetailNode,moduleNode);}
SiteMapNodeInfo有三個參數,分別對應為索引值、頁面地址、顯示標題。不過這裡又添加了寫入程式碼,可以根據自己的需要修改,使其能夠從設定檔中讀取網站映射資訊。
現在再運行程式,可以看到ProductDetail頁面已經添加在了Products業務模組下,並且頁面上已經添加了導覽列。
結束語
利用Web Client Software Factory,一個簡單的Composite Web應用程式塊和View-Presenter模式結合的使用就到這裡了。下篇我們做更接近實際的例子,如何使用View-Presenter模式進行資料繫結和ObjectContainerDataSource控制項的使用。
範例程式碼下載:/Files/Terrylee/WebClientDemo1.rar