版本:1.1 [2007-02-12]
本教程基於《NBearV3 Step by Step 教程——IoC 篇》,示範如何利用 NBearV3 的 MVP 模組實作基於 IoC 的 MVP 模式。您將看到,封裝了 NBear IoC 模組的 NBear.MVP 不僅大幅增強表現層可測試性,更能充分利用既有 IoC 模組獲得依賴注入能力與分散式服務支援。
註:建議先閱讀 IoC 篇教程以掌握 ORM 與 IoC 基礎知識。
透過本教程,讀者應能全面掌握使用 NBearV3 MVP 模組實作表現層 MVP 模式。
教程所有專案與代碼收錄於 NBearV3 最新原始碼套件(nbear.org 下載)中的 tutorials\MVP_Tutorial 目錄。
不超過 45 分鐘。
C:\,將看到 dist、doc、cases、src、tutorials 等目錄。本教程使用 dist 目錄下的 release 版 DLL 與 tutorials 中的 IoC 基礎教程。tutorials 中的整個 IoC_Tutorial 目錄複製至任意位置並重命名為 MVP_Tutorial,以 IoC 教程為基礎進行擴充。MVP 模式包含 Model、View、Presenter 三部分。在 NBear.MVP 中,Model 直接使用基於 NBear.IoC 的 Service,故僅需額外定義 View 與 Presenter 代碼。為充分解耦 M、V、P,將運用介面、泛型與 IoC 技術。
加入參考 dist\NBear.Common.dll 與 Entities 專案。新增 ISampleView.cs:
using System;
using Entities;
namespace ViewInterfaces
{
public interface ISampleView
{
int CategoryID { get; }
Category[] Categories { set; }
Product[] ProductsInCategory { set; }
}
}
加入參考 dist\NBear.Common.dll、NBear.MVP.dll 與 Entities 專案。新增 ISamplePresenter.cs:
using System;
using Entities;
using NBear.MVP;
namespace PresenterInterfaces
{
public interface ISamplePresenter : IPresenter
{
void GetCategories();
void GetProductsInCategory();
}
}
加入參考 dist\NBear.Common.dll、NBear.IoC.dll、NBear.MVP.dll、ServiceInterfaces、ViewInterfaces、PresenterInterfaces、Entities 專案。新增 SamplePresenter.cs 實作 ISamplePresenter:
using System;
using System.Collections.Generic;
using Entities;
using ServiceInterfaces;
using PresenterInterfaces;
using ViewInterfaces;
using NBear.MVP;
namespace PresenterImpls
{
public class SamplePresenter : Presenter<ISampleView, ICategoryService>, ISamplePresenter
{
#region ISamplePresenter Members
public void GetCategories()
{
// Presenter 可做額外資料過濾,讓 Service 更可重用
List<Category> categoriesWithProducts = new List<Category>();
foreach (Category item in model.GetAllCategories())
{
if (item.Products.Count > 0)
{
categoriesWithProducts.Add(item);
}
}
view.Categories = categoriesWithProducts.ToArray();
if (categoriesWithProducts.Count > 0)
{
view.ProductsInCategory = model.GetCategoryByID(categoriesWithProducts[0].CategoryID).Products.ToArray();
}
}
public void GetProductsInCategory()
{
view.ProductsInCategory = model.GetCategoryByID(view.CategoryID).Products.ToArray();
}
#endregion
}
}
說明:SamplePresenter 繼承 NBear.MVP.Presenter<TView, TModel> 基類並實作 IPresenter。基類提供對 NBear.IoC 的封裝,衍生類可存取 view 與 model 兩個 protected 成員。PresenterImpls 可像 ServiceImpls 一樣獨立測試,這是 MVP 模式最大優勢。
刪除原有 Default.aspx 及代碼,加入參考 ViewInterfaces、PresenterInterfaces、PresenterImpls(實際只需將 DLL 複製到 bin 目錄)。為 Default.aspx 加入 DropDownList、Button、GridView 控制項:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Choose a Category: <asp:DropDownList ID="listCategories" runat="server" DataTextField="CategoryName" DataValueField="CategoryID"></asp:DropDownList>
<asp:Button ID="btnLoad" runat="server" OnClick="btnLoad_Click" Text="Load Products in Selected Cateogry" /><br /><br />
<asp:GridView ID="gridProducts" runat="server"></asp:GridView>
</div>
</form>
</body>
</html>
Default.aspx.cs:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Entities;
using ViewInterfaces;
using PresenterInterfaces;
using NBear.MVP;
public partial class _Default : System.Web.UI.Page, ISampleView
{
private ISamplePresenter presenter;
protected void Page_Load(object sender, EventArgs e)
{
presenter = PresenterFactory.Create().GetPresenter<ISamplePresenter>(this);
if (!IsPostBack)
{
presenter.GetCategories();
DataBind();
}
}
protected void btnLoad_Click(object sender, EventArgs e)
{
presenter.GetProductsInCategory();
gridProducts.DataBind();
}
#region ISampleView Members
public int CategoryID
{
get
{
if (listCategories.SelectedIndex < 0) return -1;
return int.Parse(listCategories.Items[listCategories.SelectedIndex].Value);
}
}
public Category[] Categories
{
set { listCategories.DataSource = value; }
}
public Product[] ProductsInCategory
{
set { gridProducts.DataSource = value; }
}
#endregion
}
關鍵點:
PresenterFactory.Create().GetPresenter<T>(view) 透過 NBear.IoC.ServiceFactory 取得 Presenter 實作,支援分散式 IoC。PresenterInterfaces,具體實作透過 IoC 依賴注入取得,修改設定檔即可切換實作無需重新編譯。IPresenter.TypeOfModel 從 IoC 容器自動取得 Model(ServiceFactory.GetService<type>()),自動具備分散式能力。在 castle 設定區段中新增 Presenter 元件註冊:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="entityConfig" type="NBear.Common.EntityConfigurationSection, NBear.Common"/>
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"/>
</configSections>
<entityConfig>
<includes>
<add key="Sample Entity Config" value="~/EntityConfig.xml"/>
</includes>
</entityConfig>
<castle>
<components>
<component id="category service" service="ServiceInterfaces.ICategoryService, ServiceInterfaces" type="ServiceImpls.CategoryService, ServiceImpls"/>
<component id="sample presenter" service="PresenterInterfaces.ISamplePresenter, PresenterInterfaces" type="PresenterImpls.SamplePresenter, PresenterImpls"/>
</components>
</castle>
<connectionStrings>
<add name="Northwind" connectionString="Server=(local);Database=Northwind;Uid=sa;Pwd=sa" providerName="NBear.Data.SqlServer.SqlDbProvider"/>
</connectionStrings>
<system.web>...</system.web>
</configuration>
執行網站瀏覽 Default.aspx,即可看到功能。改變分類選擇並點擊按鈕即可查看不同分類下的產品。
實務上常遇到一個 Presenter 需存取多個 Model 的情況,有兩種做法:
Presenter<IView, IModel1, IModel2>、Presenter<IView, IModel1, IModel2, IModel3> 等。使用與配置方式與前文相同。Presenter<IView> 基類:需要用到任意 Model 時,直接呼叫 NBear.IoC.Service.ServiceFactory.Create().GetService<IServiceType>() 取得 Service 實例。此法更靈活,為推薦做法。正文結束