NBearV3教程——MVP(Model/View/Presenter)篇

版本

1.1 [2007-2-12]

简介

本教程在《NBearV3 Step by Step教程——IoC》的基础上,演示如何基于NBearV3MVP模块实现基于NBearIoCMVP模式的过程。您将看到,利用封装了NBearIoC模块的NBear.MVP模块,不仅能大大加强系统表现层的可测试性,同时能充分利用NBear已有的IoC模块获得依赖注入能力及基于IoC的分布式服务支持。

注:在阅读本文之前,建议读者先阅读《NBearV3 Step by Step教程——IoC》以掌握NBearV3中有关ORMIoC的基本知识。

目标

通过本教程,读者应能够全面掌握使用NBearV3MVP模块实现表现层MVP模式。

代码

本教程演示创建的所有工程和代码,包含于可以从nbear.org下载的NBearV3最新源码zip包中的tutorials\MVP_Tutorial目录中。因此,在使用本教程的过程中如有任何疑问,可以直接参考这些代码。

时间

<45分钟。

正文

Step 1 下载NBearV3最新版本及准备

1.1访问http://nbear.org,下载NBearV3的最新版本到本地目录。

1.2 将下载的zip文件解压至C:\,您将看到,加压后的NBearV3目录中包括:distdoccasessrctutorials等目录。其中,在本教程中将会使用的是dist目录中的所有release编译版本的dllexetutorials目录中之前的IoC基础教程。

1.3 tutorials目录中的整个IoC_Tutorial目录复制到任意其它位置,并命名为MVP_Tutorial,我们将以IoC_Tutorial为基础,演示NBearV3中基于IoC的分布式开发的知识。

Step 2 定义ViewPresenter

2.1 MVP_Tutorial中的IoC_Tutorial.sln重命名为MVP_Tutorial.sln,并在VS2005开发环境中打开。

2.2 我们知道MVP模式中,有ModelViewPresenter三个部分。在NBear.MVP中,Model部分,我们直接使用基于NBear.IoCService,因此,对于原来的IoC教程的代码,我们只需要额外定义ViewPresenter的代码。为了充分解耦MVP三部分,我们将用到接口、范型和IoC技术。

2.3 sln新增一个名叫ViewInterfaces的类库工程。添加该工程到dist\NBear.Common.dllEntities工程的引用。在ViewInterfaces中增加一个ISampleView.cs文件,包含如下内容:

 1using System;
 2using Entities;
 3
 4namespace ViewInterfaces
 5{
 6    public interface ISampleView
 7    {
 8        int CategoryID get; }
 9        Category[] Categories set; }
10        Product[] ProductsInCategory set; }
11    }

12}

2.4 sln新增一个名叫PresenterInterfaces的类库工程。添加该工程到dist\NBear.Common.dllNBear.MVP.dllEntities工程的引用。在PresenterInterfaces中增加一个ISamplePresenter.cs文件,包含如下内容:

 1using System;
 2using Entities;
 3using NBear.MVP;
 4
 5namespace PresenterInterfaces
 6{
 7    public interface ISamplePresenter : IPresenter
 8    {
 9        void GetCategories();
10        void GetProductsInCategory();
11    }

12}

2.5 sln新增一个名叫PresenterImpls的类库工程。添加该工程到dist\ NBear.Common.dllNBear.IoC.dllNBear.MVP.dllServiceInterfacesViewInterfacesPresenterInterfacesEntities工程的引用。在PresenterImpls中增加一个SamplePresenter.cs文件,实现前面定义的ISamplePresenter,包含如下内容:

 1using System;
 2using System.Collections.Generic;
 3using Entities;
 4using ServiceInterfaces;
 5using PresenterInterfaces;
 6using ViewInterfaces;
 7using NBear.MVP;
 8
 9namespace PresenterImpls
10{
11    public class SamplePresenter : Presenter<ISampleView, ICategoryService>, ISamplePresenter
12    {
13        ISamplePresenter Members
40    }

41}

2.6 至此,需要的View接口、Presenter接口和实现都定义完了。对PresenterImpls,可以和ServiceImpls一样进行独立的测试。这是MVP模式最大的好处。注意,PresenterImplsSamplePresenter继承自NBear.MVP中定义的Presenter基类,并实现IPresenter接口。该接口和基类为Presenter提供了对NBear.IoC的封装,在继承类中,可以访问Presenter基类中定义的viewmodel这两个protected的成员变量,分别访问关联的viewmodel。下面,我们将修改website以使用这些类。您将看到NBear.MVP通过 NBear.IoC获得的依赖注入能力。

Step 3 website中使用ViewPresenter

3.1 website工程中,先删除原来的Default.aspx和关联的Default.aspx.cs中的代码,并添加websiteViewInterfacesPresenterInterfacesPresenterImpls(其实无需添加对PresenterImpls的引用,而只需要将PresenterImpls.dll复制到websitebin目录,这里为了省区手动复制的过程才增加了它的引用)工程的引用。我们为Default.aspx增加一个DropDownList、一个Button和一个GridView控件如下:

 1<%@ 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:&nbsp;<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接口得到其实现。代码如下:

 1using System;
 2using System.Data;
 3using System.Configuration;
 4using System.Web;
 5using System.Web.Security;
 6using System.Web.UI;
 7using System.Web.UI.WebControls;
 8using System.Web.UI.WebControls.WebParts;
 9using System.Web.UI.HtmlControls;
10
11using Entities;
12using ViewInterfaces;
13using PresenterInterfaces;
14using NBear.MVP;
15
16public partial class _Default : System.Web.UI.Page, ISampleView
17{
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
70}

通过PresenterFactory类的Create()我们就能获得一个PresenterFactory类的singleton实例。通过GetPresenter()方法,传入Presenter的接口作为范型参数,页面自己this作为实现了ISampleView接口的实例的唯一的参数,就能得到需要的Presenter的实现类实例,这个内部的过程是通过NBear.IoCServiceFactory实现的,因此,可以和IoC_Adv教程中一样使用基于分布式IoCService作为Model

我们可以看到,website仅依赖于PresenterInterfacesPrensenter的具体实现通过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配置节中增加ISamplePresenterSamplePresenter的配置。修改后的Web.config代码如下:

 1<?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不需要知道何时绑定数据,不需要处理事件回调,而只需要负责对viewmodel进行数据传递、验证和过滤。何时绑定,以及哪些数据在IsPostBack时需要重新载入,哪些数据只需要在页面初次载入时载入都是由Default页面自己控制的。这样做的好处是,Presenter对具体的表现层(这里是可以PostBack的页面)没有任何概念上的依赖,做到了真正的解耦。即使要将该PresenterModel应用到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继承不含ModelPresenter<IView>基类。在Presenter具体实现类中,当需要使用任意Model(或者说Service时),只需要使用NBear.IoC.Service.ServiceFactory.Create().GetService<IServiceType>()方法就能获得需要的Service的实例,然后,就能方便的调用service的方法。很明显,使用这种方法相比方法1更灵活。因此,也是推荐的做法

//正文结束

//本文结束

0
0
(请您对文章做出评价)
« 上一篇:NBearV3.3.7 发布 - 震撼人心的VsPlugin来了!
» 下一篇:Suggested NBear Framework Based FDD Development Steps
posted @ 2006-12-20 20:07 Teddy's Knowledge Base 阅读(6139) 评论(22)  编辑 收藏 网摘 所属分类: Web Dev., NBear

  回复  引用  查看    
#1楼2006-12-20 20:50 | 浪子      
//
何时绑定,以及哪些数据在IsPostBack时需要重新载入,哪些数据只需要在页面初次载入时载入都是由Default页面自己控制的。
//

good idea,我现在把IsPostBack也带入了Presenter,导致Presenter的逻辑处理复杂度增加,而且无法直接移植到WinForm环境下,因为Presenter已经对PostBack的机制做了太多的个性化处理,使之更向是单独倾向于Web开发环境

  回复  引用  查看    
#2楼2006-12-21 09:17 | freetofly      
在MVP里边,总是对M的概念特别模糊
老大能稍微解释一下么?

  回复  引用  查看    
#3楼[楼主]2006-12-21 09:20 | Teddy's Knowledge Base      
@freetofly
M可以直接是你的BusinessModel或Service或对他们的Facade封装。

  回复  引用  查看    
#4楼2006-12-21 09:32 | freetofly      
@老大
那么在MVP的M里边,是不是挂接在P上,直接跟业务罗基层交互,而跟V里边没任何联系

  回复  引用  查看    
#5楼[楼主]2006-12-21 09:34 | Teddy's Knowledge Base      
@freetofly
是的M对V和P没有任何依赖。

  回复  引用  查看    
#6楼2006-12-22 13:32 | Dah      
@Teddy's Knowledge Base
Teddy, NBearV3有没有对实体属性的Default Value设置? 我找了半天都没找到...

  回复  引用  查看    
#7楼[楼主]2006-12-22 13:38 | Teddy's Knowledge Base      
@Dah
实体属性的Default Value必须写partial类,扩展实体的构造函数来实现。

例如:
有一个实体User有属性int Type,如果希望其默认值为3。

需要在你的Entities工程中,建一个独立的.cs文件,该文件包含下面这样的内容:

public partial class User : NBear.Common.Entity
{
public User()
{
_Type = 3;
}
}

  回复  引用    
#8楼2007-01-10 15:12 | 午休宝[未注册用户]
午休宝 门铃 午休枕 午睡枕
http://zhubaoyi.cn.alibaba.com
促销礼品 赠品 促销礼品 赠品
http://***
全世界很多人没有午休生活的习惯,还有很多人因为工作和生活的需要,只能以臂代枕伏案午休,或者直接用头和脸伏案午休,或者仰靠在座椅上歪着脑袋午休,或者躺在沙发上枕着硬硬的书本午休……,午休生活质量十分低劣,极大地影响身体健康,亟需优质的午休产品改善人们的午休生活。鉴于全世界的午休产品几乎空白,本公司枪占先机,于2004年用巨资争得了英国FORTUNE EAGLE国际投资有限公司的授权,在全球总经销该公司的“午休宝”(Nap Baby,音译名:奈普贝贝)产品,很快使“午休宝”风靡全球。

  回复  引用    
#9楼2007-01-20 12:52 | CHINA_CSDNER[未注册用户]
Nice!
but with weak event support, which is very important part in user interface

  回复  引用    
#10楼2007-02-08 15:36 | Tonyyang[未注册用户]
我用Winform測試到這里出錯,
public override EntityConfiguration GetEntityConfiguration()
{
if (_PDN_GenProducts_DEntityConfiguration == null) _PDN_GenProducts_DEntityConfiguration = MetaDataManager.GetEntityConfiguration("Leasear.Entities.PDN_GenProducts_D");
return _PDN_GenProducts_DEntityConfiguration;
}
但是在webform測試正常,請問啥原因.謝謝!

  回复  引用  查看    
#11楼[楼主]2007-02-08 15:39 | Teddy's Knowledge Base      
你的错误应该是程序找不到EntityConfig.xml文件所致,将EntityConfig.xml复制到winform的exe文件所在的Debug或Release目录应该就好了。
  回复  引用    
#12楼2007-02-08 15:55 | Tonyyang[未注册用户]
是的,我剛查到了原因,我寫上EntityConfig.xml的絕對路徑可以了
謝謝!

  回复  引用  查看    
#13楼2007-02-08 16:15 | MK2      
@Tonyyang
对,WinForm中好像不支持相对路径的,我也遇到同样的问题。

希望Teddy能对这个示例,写一些单元测试的示例。

  回复  引用  查看    
#14楼2007-02-09 01:42 | MK2      


当指定两个Service时,如 Presenter<IListView, IContentService, ICategoryService>, IListPresenter需要实现这两个接口````

public void BindModel(object model)
{
throw new Exception("The method or operation is not implemented.");
}

public Type TypeOfModel
{
get { throw new Exception("The method or operation is not implemented."); }
}

为什么指定一个Service就不用呢?

  回复  引用  查看    
#15楼2007-02-09 02:03 | MK2      
看了源代码后,发现需要继承IPresenter2,即
public interface IListPresenter : IPresenter2

就可以解决问题了。

  回复  引用    
#16楼2007-04-17 10:34 | 老胡[未注册用户]
如果是多线程的应用程序,在MVP架构中多线程应该放在那个层?
  回复  引用  查看    
#17楼2007-05-29 15:22 | Justin      
对于MVP一直有一个小问题,这里的P翻译过来是什么意思?

presenter
[pri5zentE]
n.
推荐者

是这个意思吗?有些别扭啊,远没有MVC里的C(控制器)感觉贴切。

  回复  引用    
#18楼2008-06-21 15:54 | saler[未注册用户]
ORM是什么?
对象关系映射(Object Relational Mapping,简称ORM)
详细解译尽在:www.aihua58.cn

  回复  引用    
#19楼2008-06-21 15:55 | saler[未注册用户]
ORM是什么?
对象关系映射(Object Relational Mapping,简称ORM)
详细解译尽在 :www.aihua58.cn