本教程在《NBearV3 Step by Step教程——IoC篇》的基础上,演示如何基于NBearV3的MVP模块实现基于NBear的IoC的MVP模式的过程。您将看到,利用封装了NBear的IoC模块的NBear.MVP模块,不仅能大大加强系统表现层的可测试性,同时能充分利用NBear已有的IoC模块获得依赖注入能力及基于IoC的分布式服务支持。通过本教程,读者应能够全面掌握使用NBearV3的MVP模块实现表现层MVP模式。
版本
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 文件,包含如下内容:
1 using System; 2 using Entities; 3 4 namespace 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.dll 、 NBear.MVP.dll 和 Entities 工程的引用。在 PresenterInterfaces 中增加一个 ISamplePresenter.cs 文件,包含如下内容:
1 using System; 2 using Entities; 3 using NBear.MVP; 4 5 namespace PresenterInterfaces 6 { 7 public interface ISamplePresenter : IPresenter 8 { 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 ,包含如下内容:
1 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 PresenterImpls 10 { 11 public class SamplePresenter : Presenter < ISampleView, ICategoryService > , ISamplePresenter 12 { 13 ISamplePresenter Members #region ISamplePresenter Members 14 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 #endregion 40 }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 控件如下:
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: < 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 接口得到其实现。代码如下:
1 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, 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 #region ISampleView Members 37 38 public int CategoryID 39 { 40 get 41 { 42 if (listCategories.SelectedIndex < 0 ) 43 { 44 return - 1 ; 45 }46 else 47 { 48 return int .Parse(listCategories.Items[listCategories.SelectedIndex].Value); 49 }50 }51 }52 53 public Category[] Categories 54 { 55 set 56 { 57 listCategories.DataSource = value; 58 }59 }60 61 public Product[] ProductsInCategory 62 { 63 set 64 { 65 gridProducts.DataSource = value; 66 }67 }68 69 #endregion 70 }
通过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 代码如下:
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 不需要知道何时绑定数据,不需要处理事件回调,而只需要负责对 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 更灵活。因此,也是推荐的做法 。
// 正文结束
//本文结束
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构