ASP.NET 设计模式 -- 第8章 表示层 (学习笔记)
8.1.4 StructureMap(IOC容器)
(1) StructureMap框架下载地址: https://sourceforge.net/projects/structuremap/
(2)新建解决方案ASPPatterns.Chap8.IoC,添加类库项目ASPPatterns.Chap8.IoC.Model,添加Web应用程序项目ASPPatterns.Chap8.IoC.UI.Web
(3)在Model项目中添加两个文件夹Despatch和Payment
(4)在Despatch文件夹添加IDespatchService、ICourier接口和实现接口的类DespatchService、FedExCourier。
namespace ASPPatterns.Chap8.IoC.Model.Despatch { public interface IDespatchService { } } //需要建立不同的文件 namespace ASPPatterns.Chap8.IoC.Model.Despatch { public interface ICourier { } } //需要建立不同的文件 namespace ASPPatterns.Chap8.IoC.Model.Despatch { public class DespatchService : IDespatchService { private ICourier _courier; public DespatchService(ICourier courier) { _courier = courier; } public override string ToString() { return _courier.ToString(); } } } //需要建立不同的文件 namespace ASPPatterns.Chap8.IoC.Model.Despatch { public class FedExCourier : ICourier { } }
(5)在Payment文件夹添加IPaymentGateway、IPaymentMerchant接口和实现接口的类PaymentGateway、StreamLinePaymentMerchant。
namespace ASPPatterns.Chap8.IoC.Model.Payment { public interface IPaymentGateway { } } //需要建立不同的文件 namespace ASPPatterns.Chap8.IoC.Model.Payment { public interface IPaymentMerchant { } } //需要建立不同的文件 namespace ASPPatterns.Chap8.IoC.Model.Payment { public class PaymentGateway : IPaymentGateway { IPaymentMerchant _paymentMerchant; public PaymentGateway(IPaymentMerchant paymentMerchant) { _paymentMerchant = paymentMerchant; } public override string ToString() { return _paymentMerchant.ToString(); } } } //需要建立不同的文件 namespace ASPPatterns.Chap8.IoC.Model.Payment { public class StreamLinePaymentMerchant : IPaymentMerchant { } }
(6)最后在Model项目添加新类OrderService。
using ASPPatterns.Chap8.IoC.Model.Payment; using ASPPatterns.Chap8.IoC.Model.Despatch; namespace ASPPatterns.Chap8.IoC.Model { public class OrderService { private IPaymentGateway _paymentGateway; private IDespatchService _despatchService; public OrderService(IPaymentGateway paymentGateway, IDespatchService despatchService) { _paymentGateway = paymentGateway; _despatchService = despatchService; } public override string ToString() { return String.Format("Payment Gateway: {0}, Despatch Service: {1}", _paymentGateway.ToString(), _despatchService.ToString()); } } }
(7)下一步是在ASPPatterns.Chap8.IoC.UI.Web项目上添加指向下载的StructureMap.dll引用,并添加新类BootStrapper
using StructureMap; using StructureMap.Configuration.DSL; using ASPPatterns.Chap8.IoC.Model.Payment; using ASPPatterns.Chap8.IoC.Model.Despatch; namespace ASPPatterns.Chap8.IoC.UI.Web { public class BootStrapper { public static void ConfigureStructureMap() { // Initialize the registry ObjectFactory.Initialize(x => { x.AddRegistry<ModelRegistry>(); }); } public class ModelRegistry : Registry { public ModelRegistry() { ForRequestedType<IPaymentGateway>().TheDefault.Is.OfConcreteType<PaymentGateway>(); ForRequestedType<IPaymentMerchant>().TheDefault.Is.OfConcreteType<StreamLinePaymentMerchant>(); ForRequestedType<IDespatchService>().TheDefault.Is.OfConcreteType<DespatchService>(); ForRequestedType<ICourier>().TheDefault.Is.OfConcreteType<FedExCourier>(); } } } }
ModelRegistry类经过简单的设定2,当向其询问特定类型时它将返回一个具体的实现。然后再ConfigureStructureMap方法中利用StructureMap框架来初始化Registry。
相关的方法,可以在StructureMap项目网站http://structuremap.github.com/structuremap 上找到具体的说明
(8)通常,我们希望在启动时配置依赖关系,因此向UI.Web项目中添加一个global.asax文件(如果没有),
然后再Application_Start 事件中添加调用ConfigureStructureMap方法的代码
namespace ASPPatterns.Chap8.IoC.UI.Web { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { BootStrapper.ConfigureStructureMap(); } } }
(9)最后,使用StructureMap来获取所以依赖关系均已得到满足的OrderService实例。
using ASPPatterns.Chap8.IoC.Model; using ASPPatterns.Chap8.IoC.Model.Payment; using ASPPatterns.Chap8.IoC.Model.Despatch; using StructureMap; namespace ASPPatterns.Chap8.IoC.UI.Web { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { OrderService orderService; //orderService = new OrderService(new PaymentGateway(new StreamLinePaymentMerchant()), // new DespatchService(new FedExCourier())); orderService = ObjectFactory.GetInstance<OrderService>(); Response.Write(orderService.ToString()); } } }
*注意:虽然StructureMap能够解析OrderService类型,但我们并没有显式地向StructureMap注册该类型。之所以能够这样是因为,StructureMap会根据其容器中已有的类型字典注入依赖关系。自动注入依赖关系使得我们能够充分利用容器带来的好处。客户代码能够完全不感谢定义的具体依赖关系。
8.3.1 Command模式
1.意图
Command模式定义了一个表示方法的对象,将稍后调用该方法时所需的所有信息封装起来。
(Command(命令)模式: 将一个方法封装成一个对象,并将该命令的执行与它的调用者分离.)
Command模式最适合处理的情景是,事先不知道要指向什么操作来响应请求。Command模式由4部分组成:客户端、调用者、接收者以及命令。
客户端实例化命令对象,并为其提供要给负责处理请求的接收者对象。调用者获取该命令(通过该命令的接口来引用它),再调用它的执行方法,该方法还调用接收者的特定方法来处理请求.
2.UML

- IWebCommand接口(命令)声明一个用于执行操作的接口。
- WebCommand(ConcreteCommand)是IWebCommand接口的实现。WebCommand需要一个Navigator对象(接收者),它将在该接收者上调用相应的操作。
- Navigator对象(接收者)对象知道如何处理请求(在这里它知道创建哪个视图)
- WebCommandRegistry(客户端)创建一组WebCommand对象并在创建时提供接收者。
- FrontController(调用者)使用WebCommandRegistry来获取处理请求的WebCommand,然后通过调用该WebCommand的Process方法要求它执行该请求。
3.代码示例
1.创建解决方方案ASPPatterns.Chap8.FrontController,并添加如下的类库项目:
ASPPatterns.Chap8.FrontController.Controller
ASPPatterns.Chap8.FrontController.Model
ASPPatterns.Chap8.FrontController.StubRepository
再添加Web应用程序项目后,设置各项目的依赖关系,最后Controller项目添加指向StructureMap.dll的引用。
把Model-View-Presenter练习创建的Model和StubRepository项目代码复制过来,本练习也使用相同领域模型。

2.在Controller项目中,创建如下文件夹:ActionCommands、Navigation、Request、Routing、Storage和WebCommands。
3.第一组处理的任务是负责处理Front Controller生成并用于视图的数据的存储工作。
在Storage文件夹创建接口IViewStorage,并创建该接口的默认实现ViewStorage。
namespace ASPPatterns.Chap8.FrontController.Controller.Storage { public interface IViewStorage { void Add(ViewStorageKeys key, object value); object Get(ViewStorageKeys key); } } //需要建立不同的文件 namespace ASPPatterns.Chap8.FrontController.Controller.Storage { public class ViewStorage : IViewStorage { public void Add(ViewStorageKeys key, object value) { HttpContext.Current.Items.Add(key.ToString(), value); } public object Get(ViewStorageKeys key) { return HttpContext.Current.Items[key.ToString()]; } } }
4.创建视图使用的ViewStorageFactory类
namespace ASPPatterns.Chap8.FrontController.Controller.Storage { public class ViewStorageFactory { public static IViewStorage GetStorage() { return new ViewStorage(); } } }
5.创建用来支持Model数据存储的类是ViewStorageKeys枚举类。
namespace ASPPatterns.Chap8.FrontController.Controller.Storage { public enum ViewStorageKeys { Categories, Category, Products, Product } }
至此,Storage文件夹下的视图数据存储部分相关的类已经创建完成。

6.第二组处理的任务是处理导航部分的代码,在Navigation文件夹中,首先创建可供访问的页面枚举PageDirectory。
namespace ASPPatterns.Chap8.FrontController.Controller.Navigation { public enum PageDirectory { Home, CategoryProducts, ProductDetail, MissingPage } }
7.然后创建接口IPageNavigator来导航视图,并添加接口的实现PageNavigator。
namespace ASPPatterns.Chap8.FrontController.Controller.Navigation { public interface IPageNavigator { void NavigateTo(PageDirectory page); } } //需要建立不同的文件 using System.Web; namespace ASPPatterns.Chap8.FrontController.Controller.Navigation { public class PageNavigator : IPageNavigator { public void NavigateTo(PageDirectory page) { switch (page) { case PageDirectory.Home: HttpContext.Current.Server.Transfer("~/views/Home/Index.aspx"); break; case PageDirectory.ProductDetail: HttpContext.Current.Server.Transfer("~/views/Product/ProductDetail.aspx"); break; case PageDirectory.CategoryProducts: HttpContext.Current.Server.Transfer("~/views/Product/CategoryProducts.aspx"); break; case PageDirectory.MissingPage: HttpContext.Current.Server.Transfer("~/views/Shared/404.aspx"); break; } } } }
至此,Navigation文件夹下的导航视图部分相关的类已经创建完成。

8.第三组处理的任务是从HTTP上下文中创建请求。在Request文件夹添加新类Argument,表示传递给视图的查询字符串参数。
using System; using System.Collections.Specialized; namespace ASPPatterns.Chap8.FrontController.Controller.Request { public class Argument<T> { private string _key; public Argument(string key) { _key = key; } public string Key { get { return _key; } } //这个NameValueCollection集合对象由Request.QueryString填充 public T ExtractFrom(NameValueCollection queryArguments) { try { return (T)Convert.ChangeType(queryArguments[_key], typeof(T)); } catch { return default(T); } } } }
9.添加新类ActionArguments,充当可供控制器处理的实参的只读集合。
namespace ASPPatterns.Chap8.FrontController.Controller.Request { public class ActionArguments { public static readonly Argument<int> CategoryId = new Argument<int>("categoryId"); public static readonly Argument<int> ProductId = new Argument<int>("productId"); } }
10.添加新类WebRequest,表示请求本身。
using System.Collections.Specialized; namespace ASPPatterns.Chap8.FrontController.Controller.Request { public class WebRequest { public string RequestedURL { get; set; } public NameValueCollection QueryArguments { get; set; } } }
11.我们用一个工厂WebRequestFactory将HttpContext URL转换成为一个WebRequest对象,在添加该类之前需要创建接口IWebRequestFactory。
using System.Web; namespace ASPPatterns.Chap8.FrontController.Controller.Request { public interface IWebRequestFactory { WebRequest CreateFrom(HttpContext context); } } //需要建立不同的文件 using System.Web; namespace ASPPatterns.Chap8.FrontController.Controller.Request { public class WebRequestFactory : IWebRequestFactory { public WebRequest CreateFrom(HttpContext context) { WebRequest webrequest = new WebRequest(); webrequest.RequestedURL = context.Request.Url.ToString(); webrequest.QueryArguments = context.Request.QueryString; return webrequest; } } }
至此,Request文件夹下的Request部分相关的类已经创建完成。

12.第四组处理的任务是关注如何把请求路由到正确的命令。在Routing文件夹添加新类Route。
using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.Routing { public class Route { private string _route; public Route(string route) { _route = route; } public bool Matches(WebRequest request) { return request.RequestedURL.ToLower().Contains(_route.ToLower()); } public string URL { get { return _route; } } } }
13.Route类的构造器由一个字符串路由参数,下面再添加一个类Routes,它将保存有效路由的只读集合。
namespace ASPPatterns.Chap8.FrontController.Controller.Routing { public class Routes { public static readonly Route Home = new Route("/Home.catalog"); public static readonly Route CategoryProducts = new Route("/Products.catalog"); public static readonly Route ProductDetail = new Route("/Product.catalog"); } }
至此,Routing文件夹下的路由部分相关的类已经创建完成。

13.第五组处理的任务是添加命令,它们将根据请求的视图来执行相应的动作。首先从接口开始IActionCommand
using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.ActionCommands { public interface IActionCommand { void Process(WebRequest webRequest); } }
14.分别添加GetTopSellingProductsCommand、GetCategoryCommand、GetCategoryListCommand、GetCategoryProductsCommand、GetProductDetailCommand
using ASPPatterns.Chap8.FrontController.Controller.Storage; using ASPPatterns.Chap8.FrontController.Model; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.ActionCommands { public class GetTopSellingProductsCommand : IActionCommand { private IViewStorage _storage; private ProductService _productService; public GetTopSellingProductsCommand(IViewStorage storage, ProductService productService) { _storage = storage; _productService = productService; } public void Process(WebRequest webRequest) { _storage.Add(ViewStorageKeys.Products, _productService.GetBestSellingProducts()); } } } //需要建立不同的文件 using ASPPatterns.Chap8.FrontController.Controller.Storage; using ASPPatterns.Chap8.FrontController.Model; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.ActionCommands { public class GetCategoryCommand : IActionCommand { private IViewStorage _storage; private ProductService _productService; public GetCategoryCommand(IViewStorage storage, ProductService productService) { _storage = storage; _productService = productService; } public void Process(WebRequest webRequest) { int categoryId = ActionArguments.CategoryId.ExtractFrom(webRequest.QueryArguments); Category category = _productService.GetCategoryBy(categoryId); _storage.Add(ViewStorageKeys.Category, category); } } } //需要建立不同的文件 using ASPPatterns.Chap8.FrontController.Model; using ASPPatterns.Chap8.FrontController.Controller.Storage; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.ActionCommands { public class GetCategoryListCommand : IActionCommand { private IViewStorage _storage; private ProductService _productService; public GetCategoryListCommand(IViewStorage storage, ProductService productService) { _storage = storage; _productService = productService; } public void Process(WebRequest webRequest) { _storage.Add(ViewStorageKeys.Categories, _productService.GetAllCategories()); } } } //需要建立不同的文件 using ASPPatterns.Chap8.FrontController.Model; using ASPPatterns.Chap8.FrontController.Controller.Storage; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.ActionCommands { public class GetCategoryProductsCommand : IActionCommand { private IViewStorage _storage; private ProductService _productService; public GetCategoryProductsCommand(IViewStorage storage, ProductService productService) { _storage = storage; _productService = productService; } public void Process(WebRequest webRequest) { int categoryId = ActionArguments.CategoryId.ExtractFrom(webRequest.QueryArguments); _storage.Add(ViewStorageKeys.Products, _productService.GetAllProductsIn(categoryId)); } } } //需要建立不同的文件 using ASPPatterns.Chap8.FrontController.Controller.Storage; using ASPPatterns.Chap8.FrontController.Model; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.ActionCommands { public class GetProductDetailCommand : IActionCommand { private IViewStorage _storage; private ProductService _productService; public GetProductDetailCommand(IViewStorage storage, ProductService productService) { _storage = storage; _productService = productService; } public void Process(WebRequest webRequest) { int productId = ActionArguments.ProductId.ExtractFrom(webRequest.QueryArguments); _storage.Add(ViewStorageKeys.Product, _productService.GetProductBy(productId)); } } }
至此,ActionCommands文件夹下的命令相关的类已经创建完成。

15.第六组处理的任务是需要一个Web命令把路由以及将被用来处理请求的ActionCommand集合结合起来。
添加IWebCommand接口,以及与它的实现WebCommand
using System; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.WebCommands { public interface IWebCommand { Boolean CanHandle(WebRequest webRequest); void Process(WebRequest webRequest); } } //需要建立不同的文件 using System.Collections.Generic; using ASPPatterns.Chap8.FrontController.Controller.ActionCommands; using ASPPatterns.Chap8.FrontController.Controller.Navigation; using ASPPatterns.Chap8.FrontController.Controller.Routing; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.WebCommands { public class WebCommand : IWebCommand { private IPageNavigator _navigator; private List<IActionCommand> _actionCommands; private Route _route; private PageDirectory _page; public WebCommand(IPageNavigator navigator, List<IActionCommand> actionCommands, Route route, PageDirectory page) { _navigator = navigator; _actionCommands = actionCommands; _route = route; _page = page; } public bool CanHandle(WebRequest webRequest) { return _route.Matches(webRequest); } public void Process(WebRequest webRequest) { _actionCommands.ForEach(cmd => cmd.Process(webRequest)); _navigator.NavigateTo(_page); } } }
16.为了创建WebCommand列表,需要使用命令注册表,添加新接口IWebCommandRegistry,与它的实现WebCommandRegistry。
using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.WebCommands { public interface IWebCommandRegistry { IWebCommand GetCommandFor(WebRequest webRequest); } } //需要建立不同的文件 using System.Collections.Generic; using System.Linq; using ASPPatterns.Chap8.FrontController.Controller.Request; using ASPPatterns.Chap8.FrontController.Controller.Navigation; using ASPPatterns.Chap8.FrontController.Controller.ActionCommands; using StructureMap; using ASPPatterns.Chap8.FrontController.Controller.Routing; namespace ASPPatterns.Chap8.FrontController.Controller.WebCommands { public class WebCommandRegistry : IWebCommandRegistry { private IList<IWebCommand> _webCommands = new List<IWebCommand>(); public WebCommandRegistry() { _webCommands.Add(CreateGetCategoryProductsCommand()); _webCommands.Add(CreateGetHomePageCommand()); _webCommands.Add(CreateGetProductDetailCommand()); } public IWebCommand GetCommandFor(WebRequest webRequest) { return _webCommands.FirstOrDefault(wc => wc.CanHandle(webRequest)) ?? new Display404PageCommand(ObjectFactory.GetInstance<IPageNavigator>()); } public IWebCommand CreateGetCategoryProductsCommand() { List<IActionCommand> _categoryProductsActionCommands = new List<IActionCommand>(); _categoryProductsActionCommands.Add(ObjectFactory.GetInstance<GetCategoryListCommand>()); _categoryProductsActionCommands.Add(ObjectFactory.GetInstance<GetCategoryProductsCommand>()); _categoryProductsActionCommands.Add(ObjectFactory.GetInstance<GetCategoryCommand>()); return new WebCommand( ObjectFactory.GetInstance<IPageNavigator>(), _categoryProductsActionCommands, Routes.CategoryProducts, PageDirectory.CategoryProducts); } public IWebCommand CreateGetHomePageCommand() { List<IActionCommand> _homePageActionCommands = new List<IActionCommand>(); _homePageActionCommands.Add(ObjectFactory.GetInstance<GetCategoryListCommand>()); _homePageActionCommands.Add(ObjectFactory.GetInstance<GetTopSellingProductsCommand>()); return new WebCommand( ObjectFactory.GetInstance<IPageNavigator>(), _homePageActionCommands, Routes.Home, PageDirectory.Home); } public IWebCommand CreateGetProductDetailCommand() { List<IActionCommand> _productDetailActionCommands = new List<IActionCommand>(); _productDetailActionCommands.Add(ObjectFactory.GetInstance<GetCategoryListCommand>()); _productDetailActionCommands.Add(ObjectFactory.GetInstance<GetProductDetailCommand>()); return new WebCommand( ObjectFactory.GetInstance<IPageNavigator>(), _productDetailActionCommands, Routes.ProductDetail, PageDirectory.ProductDetail); } } }
17.Display404PageCommand的作用是再未能找到能够处理WebRequest的WebCommand时,将顾客重定向到一个错误信息页面。
using ASPPatterns.Chap8.FrontController.Controller.Navigation; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller.WebCommands { public class Display404PageCommand : IWebCommand { private IPageNavigator _navigator; public Display404PageCommand(IPageNavigator navigator) { _navigator = navigator; } public bool CanHandle(WebRequest webRequest) { return true; } public void Process(WebRequest webRequest) { _navigator.NavigateTo(PageDirectory.MissingPage); } } }
至此,WebCommands文件夹下的命令以及路由结合相关的类已经创建完成。

18.第七组处理的任务是创建Front Controller,添加新类FrontController
using ASPPatterns.Chap8.FrontController.Controller.WebCommands; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller { public class FrontController { IWebCommandRegistry _webCommandRegistry; public FrontController(IWebCommandRegistry webCommandRegistry) { _webCommandRegistry = webCommandRegistry; } public void handle(WebRequest request) { _webCommandRegistry.GetCommandFor(request).Process(request); } } }
19.添加IHttpHandler接口的自定义实现CustomHTTPHandler
using System.Web; using ASPPatterns.Chap8.FrontController.Controller.Request; using StructureMap; namespace ASPPatterns.Chap8.FrontController.Controller { public class CustomHTTPHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { ObjectFactory.GetInstance<FrontController>() .handle(ObjectFactory.GetInstance<IWebRequestFactory>().CreateFrom(context)); } public bool IsReusable { get { return true; } } } }
在ProcessRequest方法中使用StructureMaps ObjectFactory来获取一个FrontController实例,然后又使用该实例来处理由IWebRequestFactory接口实现(也是由StructureMaps ObjectFactory解析)所创建的WebRequest对象。
20.需要创建几个配套类,首先用来配置StructureMap的依赖关系设置的类BootStrapper
using ASPPatterns.Chap8.FrontController.Controller.Storage; using ASPPatterns.Chap8.FrontController.StubRepository; using ASPPatterns.Chap8.FrontController.Model; using ASPPatterns.Chap8.FrontController.Controller.WebCommands; using ASPPatterns.Chap8.FrontController.Controller.Navigation; using StructureMap; using StructureMap.Configuration.DSL; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller { public class BootStrapper { public static void ConfigureDependencies() { ObjectFactory.Initialize(x => { x.AddRegistry<ControllerRegistry>(); }); } public class ControllerRegistry : Registry { public ControllerRegistry() { ForRequestedType<ICategoryRepository>().TheDefault.Is.OfConcreteType<CategoryRepository>(); ForRequestedType<IProductRepository>().TheDefault.Is.OfConcreteType<ProductRepository>(); ForRequestedType<IViewStorage>().TheDefault.Is.OfConcreteType<ViewStorage>(); ForRequestedType<IPageNavigator>().TheDefault.Is.OfConcreteType<PageNavigator>(); ForRequestedType<IWebCommandRegistry>().TheDefault.Is.OfConcreteType<WebCommandRegistry>(); ForRequestedType<IWebRequestFactory>().TheDefault.Is.OfConcreteType<WebRequestFactory>(); } } } }
21.添加负责协助创建视图的URL类
using ASPPatterns.Chap8.FrontController.Model; using ASPPatterns.Chap8.FrontController.Controller.Routing; using ASPPatterns.Chap8.FrontController.Controller.Request; namespace ASPPatterns.Chap8.FrontController.Controller { public static class UrlHelper { public static string BuildHomePageLink() { return Routes.Home.URL; } public static string BuildProductDetailLinkFor(Product product) { return Routes.ProductDetail.URL + "?" + ActionArguments.ProductId.Key + "=" + product.Id; } public static string BuildProductCategoryLinkFor(Category category) { return Routes.CategoryProducts.URL + "?" + ActionArguments.CategoryId.Key + "=" + category.Id; } } }
至此,Controller项目已经创建完成。

22.接下来处理ASPPatterns.Chap8.FrontController.UI.Web项目相关的
首先Default.aspx页面,修改Page_Load事件,重定向到Home.catalog。
using System; using System.Web.UI; namespace ASPPatterns.Chap8.FrontController.UI.Web { public partial class Default : Page { protected void Page_Load(object sender, EventArgs e) { Response.Redirect("Home.catalog"); } } }
23.UI.Web项目添加Global.asax文件(如果没有的话),在Application_Start事件中调用BootStrapper类。
using System; using ASPPatterns.Chap8.FrontController.Controller; namespace ASPPatterns.Chap8.FrontController.UI.Web { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { BootStrapper.ConfigureDependencies(); } } }
24.最后UI.Web项目修改配置文件Web.config
<configuration> <system.web> <httpHandlers> <add verb="*" path="*.catalog" validate="false" type="ASPPatterns.Chap8.FrontController.Controller.CustomHTTPHandler, ASPPatterns.Chap8.FrontController.Controller"/> </httpHandlers> <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/> </system.web> </configuration>
8.3.2 Chain of Responsibility模式
1.意图
Chain of Responsibility模式的意图是,通过让多个对象有机会处理请求从而避免将请求的发送者与它的接收者耦合起来。该模式的实现方法是,把接收对象链接在一起并将对象沿着该链条传递,直到找到一个能够处理该请求的对象。
2.代码示例
1.创建解决方案ASPPatterns.Chap8.CoR,把之前的ASPPatterns.Chap8.FrontController项目复制一份,并向Controller项目添加文件夹Handlers
向文件夹Handlers添加新抽象类RequestHandler
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ASPPatterns.Chap8.CoR.Controller.Request; namespace ASPPatterns.Chap8.CoR.Controller.Handlers { public abstract class RequestHandler { protected RequestHandler _nextHandler; public RequestHandler SetNextHandler(RequestHandler requestHandler) { _nextHandler = requestHandler; return _nextHandler; } public abstract void Handle(WebRequest request); } }
2.所有请求处理程序都继承这个类,分别为各个视图添加相关处理类CategoryProductsPageHandler、HomePageHandler、PageNotFoundHandler、ProductDetailPageHandler
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ASPPatterns.Chap8.CoR.Model; using ASPPatterns.Chap8.CoR.Controller.Storage; using ASPPatterns.Chap8.CoR.Controller.Routing; using ASPPatterns.Chap8.CoR.Controller.Navigation; using ASPPatterns.Chap8.CoR.Controller.Request; namespace ASPPatterns.Chap8.CoR.Controller.Handlers { public class CategoryProductsPageHandler : RequestHandler { private Route _route; private ProductService _productService; private IViewStorage _viewStorage; private IPageNavigator _pageNavigator; public CategoryProductsPageHandler(Route route, ProductService productService, IViewStorage viewStorage, IPageNavigator pageNavigator) { _route = route; _productService = productService; _viewStorage = viewStorage; _pageNavigator = pageNavigator; } public override void Handle(WebRequest request) { if (_route.Matches(request)) { int categoryId = ActionArguments.CategoryId.ExtractFrom(request.QueryArguments); IEnumerable<Category> categories = _productService.GetAllCategories(); _viewStorage.Add(ViewStorageKeys.Categories, categories); Category category = _productService.GetCategoryBy(categoryId); _viewStorage.Add(ViewStorageKeys.Category, category); IEnumerable<Product> products = _productService.GetAllProductsIn(categoryId); _viewStorage.Add(ViewStorageKeys.Products, products); _pageNavigator.NavigateTo(PageDirectory.CategoryProducts); } else base._nextHandler.Handle(request); } } } //需要建立不同的文件 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using ASPPatterns.Chap8.CoR.Model; using ASPPatterns.Chap8.CoR.Controller.Storage; using ASPPatterns.Chap8.CoR.Controller.Routing; using ASPPatterns.Chap8.CoR.Controller.Navigation; using ASPPatterns.Chap8.CoR.Controller.Request; namespace ASPPatterns.Chap8.CoR.Controller.Handlers { public class HomePageHandler : RequestHandler { private Route _route; private ProductService _productService; private IViewStorage _viewStorage; private IPageNavigator _pageNavigator; public HomePageHandler(Route route, ProductService productService, IViewStorage viewStorage, IPageNavigator pageNavigator) { _route = route; _productService = productService; _viewStorage = viewStorage; _pageNavigator = pageNavigator; } public override void Handle(WebRequest request) { if (_route.Matches(request)) { IEnumerable<Category> categories = _productService.GetAllCategories(); _viewStorage.Add(ViewStorageKeys.Categories, categories); IEnumerable<Product> products = _productService.GetBestSellingProducts(); _viewStorage.Add(ViewStorageKeys.Products, products); _pageNavigator.NavigateTo(PageDirectory.Home); } else base._nextHandler.Handle(request); } } } //需要建立不同的文件 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using ASPPatterns.Chap8.CoR.Model; using ASPPatterns.Chap8.CoR.Controller.Storage; using ASPPatterns.Chap8.CoR.Controller.Navigation; using ASPPatterns.Chap8.CoR.Controller.Request; namespace ASPPatterns.Chap8.CoR.Controller.Handlers { public class PageNotFoundHandler : RequestHandler { private ProductService _productService; private IViewStorage _viewStorage; private IPageNavigator _pageNavigator; public PageNotFoundHandler(ProductService productService, IViewStorage viewStorage, IPageNavigator pageNavigator) { _productService = productService; _viewStorage = viewStorage; _pageNavigator = pageNavigator; } public override void Handle(WebRequest request) { _pageNavigator.NavigateTo(PageDirectory.MissingPage); } } } //需要建立不同的文件 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using ASPPatterns.Chap8.CoR.Model; using ASPPatterns.Chap8.CoR.Controller.Storage; using ASPPatterns.Chap8.CoR.Controller.Routing; using ASPPatterns.Chap8.CoR.Controller.Navigation; using ASPPatterns.Chap8.CoR.Controller.Request; namespace ASPPatterns.Chap8.CoR.Controller.Handlers { public class ProductDetailPageHandler : RequestHandler { private Route _route; private ProductService _productService; private IViewStorage _viewStorage; private IPageNavigator _pageNavigator; public ProductDetailPageHandler(Route route, ProductService productService, IViewStorage viewStorage, IPageNavigator pageNavigator) { _route = route; _productService = productService; _viewStorage = viewStorage; _pageNavigator = pageNavigator; } public override void Handle(WebRequest request) { if (_route.Matches(request)) { int productId = ActionArguments.ProductId.ExtractFrom(request.QueryArguments); IEnumerable<Category> categories = _productService.GetAllCategories(); _viewStorage.Add(ViewStorageKeys.Categories, categories); Product product = _productService.GetProductBy(productId); _viewStorage.Add(ViewStorageKeys.Product, product); _pageNavigator.NavigateTo(PageDirectory.ProductDetail); } else base._nextHandler.Handle(request); } } }
如果Route匹配WebRequest,那么该处理程序处理该请求,否则把该WebRequest对象传给下一个处理程序进行处理。
3.为了使RequestHandler来构建Chain of Responsibility,需要一个工厂方法,在文件夹Handlers添加接口IHandlerFactory
using System; namespace ASPPatterns.Chap8.CoR.Controller.Handlers { public interface IHandlerFactory { RequestHandler GetHandlers(); } }
4.添加新类HandlerFactory,实现接口IHandlerFactory
using System; using System.Collections.Generic; using System.Linq; using System.Text; using StructureMap; using ASPPatterns.Chap8.CoR.Controller.Routing; using ASPPatterns.Chap8.CoR.Model; using ASPPatterns.Chap8.CoR.Controller.Storage; using ASPPatterns.Chap8.CoR.Controller.Navigation; namespace ASPPatterns.Chap8.CoR.Controller.Handlers { public class HandlerFactory : IHandlerFactory { public RequestHandler GetHandlers() { RequestHandler handler = GetHomePageHandler(); handler .SetNextHandler(GetCategoryProductsPageHandler()) .SetNextHandler(GetProductDetailPageHandler()) .SetNextHandler(GetPageNotFoundHandler()); return handler; } private RequestHandler GetPageNotFoundHandler() { return new PageNotFoundHandler( ObjectFactory.GetInstance<ProductService>(), ObjectFactory.GetInstance<IViewStorage>(), ObjectFactory.GetInstance<IPageNavigator>()); } private RequestHandler GetProductDetailPageHandler() { return new ProductDetailPageHandler( Routes.ProductDetail, ObjectFactory.GetInstance<ProductService>(), ObjectFactory.GetInstance<IViewStorage>(), ObjectFactory.GetInstance<IPageNavigator>()); } private RequestHandler GetCategoryProductsPageHandler() { return new CategoryProductsPageHandler( Routes.CategoryProducts, ObjectFactory.GetInstance<ProductService>(), ObjectFactory.GetInstance<IViewStorage>(), ObjectFactory.GetInstance<IPageNavigator>()); } private RequestHandler GetHomePageHandler() { return new HomePageHandler( Routes.Home, ObjectFactory.GetInstance<ProductService>(), ObjectFactory.GetInstance<IViewStorage>(), ObjectFactory.GetInstance<IPageNavigator>()); } } }
5.最后修改FrontController类以接受IHandlerFactory而不是IWebCommandRegistry.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ASPPatterns.Chap8.CoR.Controller.Handlers; using ASPPatterns.Chap8.CoR.Controller.Request; namespace ASPPatterns.Chap8.CoR.Controller { public class FrontController { RequestHandler _requestHandler; public FrontController(IHandlerFactory handlerFactory) { _requestHandler = handlerFactory.GetHandlers(); } public void handle(WebRequest request) { _requestHandler.Handle(request); } } }
6.为了完成向Chain of Responsibility模式(而不是Command模式)的转换,需要更新BootStrapper类来注册IHandlerFactory的默认实例
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ASPPatterns.Chap8.CoR.Controller.Handlers; using ASPPatterns.Chap8.CoR.Model; using ASPPatterns.Chap8.CoR.StubRepository; using ASPPatterns.Chap8.CoR.Controller.Storage; using ASPPatterns.Chap8.CoR.Controller.Routing; using ASPPatterns.Chap8.CoR.Controller.Navigation; using StructureMap; using StructureMap.Configuration.DSL; using ASPPatterns.Chap8.CoR.Controller.Request; namespace ASPPatterns.Chap8.CoR.Controller { public class BootStrapper { public static void ConfigureDependencies() { // Initialize the registry ObjectFactory.Initialize(x => { x.AddRegistry<ControllerRegistry>(); }); } public class ControllerRegistry : Registry { public ControllerRegistry() { ForRequestedType<ICategoryRepository>().TheDefault.Is.OfConcreteType<CategoryRepository>(); ForRequestedType<IProductRepository>().TheDefault.Is.OfConcreteType<ProductRepository>(); ForRequestedType<IViewStorage>().TheDefault.Is.OfConcreteType<ViewStorage>(); ForRequestedType<IPageNavigator>().TheDefault.Is.OfConcreteType<PageNavigator>(); ForRequestedType<IWebRequestFactory>().TheDefault.Is.OfConcreteType<WebRequestFactory>(); ForRequestedType<IHandlerFactory>().TheDefault.Is.OfConcreteType<HandlerFactory>(); } } } }
浙公网安备 33010602011771号