版本
1.1 [2007-2-12]
简介
本教程在《NBearV3 Step by Step教程——IoC篇》的基础上,演示如何基于NBearV3的MVP模块实现基于NBear的IoC的MVP模式的过程。您将看到,利用封装了NBear的IoC模块的NBear.MVP模块,不仅能大大加强系统表现层的可测试性,同时能充分利用NBear已有的IoC模块获得依赖注入能力及基于IoC的分布式服务支持。
注:在阅读本文之前,建议读者先阅读《NBearV3 Step by Step教程——IoC篇》以掌握NBearV3中有关ORM和IoC的基本知识。
目标
通过本教程,读者应能够全面掌握使用NBearV3的MVP模块实现表现层MVP模式。
代码
本教程演示创建的所有工程和代码,包含于可以从nbear.org下载的NBearV3最新源码zip包中的tutorials/MVP_Tutorial目录中。因此,在使用本教程的过程中如有任何疑问,可以直接参考这些代码。
时间
<45分钟。
正文
Step 1 下载NBearV3最新版本及准备
1.1访问http://nbear.org,下载NBearV3的最新版本到本地目录。
1.2 将下载的zip文件解压至C:/,您将看到,加压后的NBearV3目录中包括:dist、doc、cases、src、tutorials等目录。其中,在本教程中将会使用的是dist目录中的所有release编译版本的dll和exe和tutorials目录中之前的IoC基础教程。
1.3 将tutorials目录中的整个IoC_Tutorial目录复制到任意其它位置,并命名为MVP_Tutorial,我们将以IoC_Tutorial为基础,演示NBearV3中基于IoC的分布式开发的知识。
Step 2 定义View和Presenter
2.1 将MVP_Tutorial中的IoC_Tutorial.sln重命名为MVP_Tutorial.sln,并在VS2005开发环境中打开。
2.2 我们知道MVP模式中,有Model、View和Presenter三个部分。在NBear.MVP中,Model部分,我们直接使用基于NBear.IoC的Service,因此,对于原来的IoC教程的代码,我们只需要额外定义View和Presenter的代码。为了充分解耦M、V、P三部分,我们将用到接口、范型和IoC技术。
2.3 为sln新增一个名叫ViewInterfaces的类库工程。添加该工程到dist/NBear.Common.dll和Entities工程的引用。在ViewInterfaces中增加一个ISampleView.cs文件,包含如下内容:
using
System;2
using
Entities;3

4
namespace
ViewInterfaces5

{6
public interface ISampleView7

{8

int CategoryID
{ get; }9

Category[] Categories
{ set; }10

Product[] ProductsInCategory
{ set; }11
}12
}
2.4 为sln新增一个名叫PresenterInterfaces的类库工程。添加该工程到dist/NBear.Common.dll、NBear.MVP.dll和Entities工程的引用。在PresenterInterfaces中增加一个ISamplePresenter.cs文件,包含如下内容:
using
System;2
using
Entities;3
using
NBear.MVP;4

5
namespace
PresenterInterfaces6

{7
public interface ISamplePresenter : IPresenter8

{9
void GetCategories();10
void GetProductsInCategory();11
}12
}
2.5 为sln新增一个名叫PresenterImpls的类库工程。添加该工程到dist/ NBear.Common.dll、NBear.IoC.dll、NBear.MVP.dll、ServiceInterfaces、ViewInterfaces、PresenterInterfaces和Entities工程的引用。在PresenterImpls中增加一个SamplePresenter.cs文件,实现前面定义的ISamplePresenter,包含如下内容:
using
System;2
using
System.Collections.Generic;3
using
Entities;4
using
ServiceInterfaces;5
using
PresenterInterfaces;6
using
ViewInterfaces;7
using
NBear.MVP;8

9
namespace
PresenterImpls10

{11
public class SamplePresenter : Presenter<ISampleView, ICategoryService>, ISamplePresenter12

{13

ISamplePresenter Members#region ISamplePresenter Members14

15
public void GetCategories()16

{17
//in presenter we can to additional data filtering, so that services can be reused more.18
List<Category> categoriesWithProducts = new List<Category>();19
foreach (Category item in model.GetAllCategories())20

{21
if (item.Products.Count > 0)22

{23
categoriesWithProducts.Add(item);24
}25
}26
view.Categories = categoriesWithProducts.ToArray();27

28
if (categoriesWithProducts.Count > 0)29

{30
view.ProductsInCategory = model.GetCategoryByID(categoriesWithProducts[0].CategoryID).Products.ToArray();31
}32
}33

34
public void GetProductsInCategory()35

{36
view.ProductsInCategory = model.GetCategoryByID(view.CategoryID).Products.ToArray();37
}38

39
#endregion40
}41
}
2.6 至此,需要的View接口、Presenter接口和实现都定义完了。对PresenterImpls,可以和ServiceImpls一样进行独立的测试。这是MVP模式最大的好处。注意,PresenterImpls中SamplePresenter继承自NBear.MVP中定义的Presenter基类,并实现IPresenter接口。该接口和基类为Presenter提供了对NBear.IoC的封装,在继承类中,可以访问Presenter基类中定义的view和model这两个protected的成员变量,分别访问关联的view和model。下面,我们将修改website以使用这些类。您将看到NBear.MVP通过 NBear.IoC获得的依赖注入能力。
Step 3 在website中使用View和Presenter
3.1 在website工程中,先删除原来的Default.aspx和关联的Default.aspx.cs中的代码,并添加website到ViewInterfaces、PresenterInterfaces和PresenterImpls(其实无需添加对PresenterImpls的引用,而只需要将PresenterImpls.dll复制到website的bin目录,这里为了省区手动复制的过程才增加了它的引用)工程的引用。我们为Default.aspx增加一个DropDownList、一个Button和一个GridView控件如下:
<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"
%>
2

3
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
4

5
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
6
<
head
runat
="server"
>
7
<
title
>
Untitled Page
</
title
>
8
</
head
>
9
<
body
>
10
<
form
id
="form1"
runat
="server"
>
11
<
div
>
12
Choose a Category:
<
asp:DropDownList
ID
="listCategories"
runat
="server"
DataTextField
="CategoryName"
13
DataValueField
="CategoryID"
>
14
</
asp:DropDownList
>
15
<
asp:Button
ID
="btnLoad"
runat
="server"
OnClick
="btnLoad_Click"
Text
="Load Products in Selected Cateogry"
/><
br
/>
16
<
br
/>
17
<
asp:GridView
ID
="gridProducts"
runat
="server"
>
18
</
asp:GridView
>
19
</
div
>
20
</
form
>
21
</
body
>
22
</
html
>
3.2 在Default.aspx.cs中,我们需要实现前面定义的ISampleView接口,并使用我们已经定义的SamplePresenter。和使用NBear.IoC 中的ServiceFactory类似,我们可以非常简单的使用NBear.MVP中定义的PresenterFactory类,通过Presenter接口得到其实现。代码如下:
using
System;2
using
System.Data;3
using
System.Configuration;4
using
System.Web;5
using
System.Web.Security;6
using
System.Web.UI;7
using
System.Web.UI.WebControls;8
using
System.Web.UI.WebControls.WebParts;9
using
System.Web.UI.HtmlControls;10

11
using
Entities;12
using
ViewInterfaces;13
using
PresenterInterfaces;14
using
NBear.MVP;15

16
public
partial
class
_Default : System.Web.UI.Page, ISampleView17

{18
private ISamplePresenter presenter;19

20
protected void Page_Load(object sender, EventArgs e)21

{22
presenter = PresenterFactory.Create().GetPresenter<ISamplePresenter>(this);23
if (!IsPostBack)24

{25
presenter.GetCategories();26
DataBind();27
}28
}29

30
protected void btnLoad_Click(object sender, EventArgs e)31

{32
presenter.GetProductsInCategory();33
gridProducts.DataBind();34
}35

36

ISampleView Members#region ISampleView Members37

38
public int CategoryID39

{40
get41

{42
if (listCategories.SelectedIndex < 0)43

{44
return -1;45
}46
else47

{48
return int.Parse(listCategories.Items[listCategories.SelectedIndex].Value);49
}50
}51
}52

53
public Category[] Categories54

{55
set56

{57
listCategories.DataSource = value;58
}59
}60

61
public Product[] ProductsInCategory62

{63
set64

{65
gridProducts.DataSource = value;66
}67
}68

69
#endregion70
}
通过PresenterFactory类的Create()我们就能获得一个PresenterFactory类的singleton实例。通过GetPresenter()方法,传入Presenter的接口作为范型参数,页面自己this作为实现了ISampleView接口的实例的唯一的参数,就能得到需要的Presenter的实现类实例,这个内部的过程是通过NBear.IoC的ServiceFactory实现的,因此,可以和IoC_Adv教程中一样使用基于分布式IoC的Service作为Model。
我们可以看到,website仅依赖于PresenterInterfaces,Prensenter的具体实现通过IoC以依赖注入方式获得。这样,我们可以方便地仅修改配置文件(无需重新编译)就改变Presenter接口对应的具体的Presenter实现类。
特别注意,我们在使用SamplePresenter时完全没有指定Model的位置,那么SamplePresenter怎么知道哪个model对应当前的view呢?他会通过IPresenter接口(所有的Presenter需要实现该接口)的TypeOfModel属性返回的type,通过NBear.IoC.Service.ServiceFactory.GetService<type>()从IoC容器中自动获得的。也因此,它自动获得了分布式能力。
3.3 在Web.config中的castle配置节中,因为我们的程序只需要用到category service,我们可以将product service删掉。但是,我们要在castle配置节中增加ISamplePresenter和SamplePresenter的配置。修改后的Web.config代码如下:
<?
xml version="1.0"
?>
2
<
configuration
>
3
<
configSections
>
4
<
section
name
="entityConfig"
type
="NBear.Common.EntityConfigurationSection, NBear.Common"
/>
5
<
section
name
="castle"
type
="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"
/>
6
</
configSections
>
7
<
entityConfig
>
8
<
includes
>
9
<
add
key
="Sample Entity Config"
value
="~/EntityConfig.xml"
/>
10
</
includes
>
11
</
entityConfig
>
12
<
castle
>
13
<
components
>
14
<!--
You can use standard castle component decleration schema to define service interface impls here
-->
15
<
component
id
="category service"
service
="ServiceInterfaces.ICategoryService, ServiceInterfaces"
type
="ServiceImpls.CategoryService, ServiceImpls"
/>
16
<component id="sample presenter" service="PresenterInterfaces.ISamplePresenter, PresenterInterfaces" type="PresenterImpls.SamplePresenter, PresenterImpls"/>
17
</
components
>
18
</
castle
>
19
<
appSettings
/>
20
<
connectionStrings
>
21
<
add
name
="Northwind"
connectionString
="Server=(local);Database=Northwind;Uid=sa;Pwd=sa"
providerName
="NBear.Data.SqlServer.SqlDbProvider"
/>
22
</
connectionStrings
>
23
<
system
.web
>
24
<
compilation
debug
="true"
>
25
<
assemblies
>
26
<
add
assembly
="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"
/>
27
<
add
assembly
="System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"
/>
28
<
add
assembly
="System.Runtime.Remoting, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"
/></
assemblies
></
compilation
>
29
<
authentication
mode
="Windows"
/>
30
</
system.web
>
31
</
configuration
>
3.4 运行website并浏览Default.aspx页面,我们就可以看到我们实现的功能了。改变Category的选择,点击按钮就能查看不同的Category下的Products。
后记
如果看过其他MVP模式的实现,读者可能会注意到某些区别。在这里的实现中,Presenter不需要知道何时绑定数据,不需要处理事件回调,而只需要负责对view和model进行数据传递、验证和过滤。何时绑定,以及哪些数据在IsPostBack时需要重新载入,哪些数据只需要在页面初次载入时载入都是由Default页面自己控制的。这样做的好处是,Presenter对具体的表现层(这里是可以PostBack的页面)没有任何概念上的依赖,做到了真正的解耦。即使要将该Presenter和Model应用到WindowsForm或者WPF表现层,也是轻而易举的。
V1.1新增 –Presenter何如使用到多个Model?
上面的示例中的Presenter是比较典型的一个View对应Model(或者说Service)的情况。在实际的开发中,经常会出现一个Presenter需要访问到多个Model的情况。
此时,可以有两种可选的做法:
1、 具体的Presenter继承含多个Model泛型参数的Presenter基类,如:Presenter<IView, IModel1, IModel2>, Presenter<IView, IModel1, IModel2, IModel3>等。使用和配置Presenter的方法和前文讨论的方法完全一致。
2、 具体的Presenter继承不含Model的Presenter<IView>基类。在Presenter具体实现类中,当需要使用任意Model(或者说Service时),只需要使用NBear.IoC.Service.ServiceFactory.Create().GetService<IServiceType>()方法就能获得需要的Service的实例,然后,就能方便的调用service的方法。很明显,使用这种方法相比方法1更灵活。因此,也是推荐的做法。
//正文结束
//本文结束

本教程介绍如何使用 NBearV3 的 MVP 模块实现表现层的 MVP 模式,涵盖 View 和 Presenter 的定义、依赖注入以及分布式服务支持等内容。
573

被折叠的 条评论
为什么被折叠?



