Asp.netMVC示例项目"Suteki.Shop"分析之---Controller 在上文中,介绍了如何安装和使用Suteki,今天我们通过源码来看一下Suteki是如何使用 Controller。
在Suteki中,其使用Abstract的方式来定义一个ControllerBase,以此作为所有Controller 的基类,下面是其Controller的类设计图:
在该基类中定义了一些Controller中常用到的方法,比如为当前视图添加MetaDescription, Title等:
复制代码 [Rescue("Default"),Authenticate,CopyMessageFromTempDataToViewData] publicabstractclassControllerBase:Controller,IProvidesBaseService { privateIBaseControllerServicebaseControllerService;
/// ///Suppliesservicesandconfigurationtoallcontrollers /// publicIBaseControllerServiceBaseControllerService { get{returnbaseControllerService;} set { baseControllerService=value;
ViewData["Title"]="{0}{1}".With( baseControllerService.ShopName, GetControllerName());
ViewData["MetaDescription"]="\"{0}\"".With(baseControllerService.MetaDescription); } }
publicILoggerLogger{get;set;}
publicvirtualstringGetControllerName() { return"-{0}".With(GetType().Name.Replace("Controller","")); }
publicvirtualvoidAppendTitle(stringtext) { ViewData["Title"]="{0}-{1}".With(ViewData["Title"],text); }
publicvirtualvoidAppendMetaDescription(stringtext) { ViewData["MetaDescription"]=text; }
publicstringMessage { get{returnTempData["message"]asstring;} set{TempData["message"]=value;} }
protectedoverridevoidOnException(ExceptionContextfilterContext){ Response.Clear(); base.OnException(filterContext); } } 复制代码
当然,细心的朋友发现了该抽象类中还包括一个IBaseControllerService接口实例。 该接口的主要定义了一些网店系统信息,如店铺名称,版权信息,Email信息等,如下:
复制代码 publicinterfaceIBaseControllerService { IRepositoryCategoryRepository{get;} stringGoogleTrackingCode{get;set;} stringShopName{get;set;} stringEmailAddress{get;set;} stringSiteUrl{get;} stringMetaDescription{get;set;} stringCopyright{get;set;} stringPhoneNumber{get;set;} stringSiteCss{get;set;} } 复制代码
而作为唯一一个实现了该接口的子类“BaseControllerService”定义如下:
复制代码 publicclassBaseControllerService:IBaseControllerService { publicIRepositoryCategoryRepository{get;privateset;} publicstringGoogleTrackingCode{get;set;} publicstringMetaDescription{get;set;} privatestringshopName; privatestringemailAddress; privatestringcopyright; privatestringphoneNumber; privatestringsiteCss;
.. } 复制代码
而初始化BaseControllerService实例并将配置文件中的信息绑定到该类实例中的操作交给了Windsor, 该组件在Castle中用于实现IOC操作,其配置文件位于项目Suteki.Shop\Configuration\Windsor.config. 下面是其配置结点内容:
复制代码 id="IBaseControllerService:test.jumpthegun.co.uk" service="Suteki.Shop.Services.IBaseControllerService,Suteki.Shop" type="Suteki.Shop.Services.BaseControllerService,Suteki.Shop" lifestyle="transient">
SutekiShop info@sutekishop.co.uk UA-1643677-4 SutekiShopisanewselfserviceeCommercesolution.Searchengineoptimisedwww.wang027.comandfullycustomisable Site.css
复制代码
这类就完成了把网店的系统信息绑定到Controller中的操作,而Controller就会在其基类中将相关的 信息绑定到ViewData中,如下:
ViewData["Title"]="{0}{1}".With(baseControllerService.ShopName,GetControllerName()); ViewData["MetaDescription"]="\"{0}\"".With(baseControllerService.MetaDescription);
到这里,其实大家应该发现这种对Controller的处理与我们以前所使用的PageBase方式相似,就是将 项目中所有的Page都继承自PageBase,然后在相应的Page中引用PageBase中定义的属性和方法。 有了ControllerBase,我们看一下在相应的子Controller中是如何使用的,这里有一个例子, ProductController(位于Suteki.Shop\Controllers\ProductController.cs):
复制代码 publicclassProductController:ControllerBase {
publicActionResultItem(stringurlName) { returnRenderItemView(urlName); } ..
ActionResultRenderItemView(stringurlName) { varproduct=productRepository.GetAll().WithUrlName(urlName); AppendTitle(product.Name); AppendMetaDescription(product.Description); returnView("Item",ShopView.Data.WithProduct(product)); }
} 复制代码
该Controller中的Action:"Item"调用了RenderItemView()就是使用了基类中的AppendTitle, AppendMetaDescription。下面是其运行时的截图:
除了上面所说的这种ControllerBase方式,Suteki.Shop还使用了Controller方式来实现对 一些公用Action的操作,比如列表,编辑,添加记录,调整记录上下位置等。而这块实现代码被放 置在了Suteki.Common\ScaffoldController.cs和OrderableScaffoldController.cs文件中,其中 ScaffoldController为父类,其中包括列表,编辑,添加Action等。
Code
大家请注意ScaffoldController类中的几个公共属性:
publicIRepositoryRepository{get;set;} publicIRepositoryResolverrepositoryResolver{get;set;} publicIValidatingBinderValidatingBinder{get;set;} publicIHttpContextServicehttpContextService{get;set;}
其中Repository是一些对数据CRUD的操作对象,下面是Repository中的一些接口成员方法:
复制代码 publicinterfaceIRepositorywhereT:class { TGetById(intid); IQueryableGetAll(); voidInsertOnSubmit(Tentity); voidDeleteOnSubmit(Tentity); [Obsolete("UnitsofWorkshouldbemanagedexternallytotheRepository.")] voidSubmitChanges(); } 复制代码
这样就可以在ScaffoldController使用统一的接口函数调用相应子类中的实现方法了。
而ScaffoldController的子类OrderableScaffoldController则实现了对数据集合中的某行元素 上下移动的操作:
复制代码 publicclassOrderableScaffoldController:ScaffoldControllerwhereT:class,IOrderable,new() { publicIOrderableServiceOrderableService{get;set;}
protectedoverrideActionResultRenderIndexView(int?page) { varitems=Repository.GetAll().InOrder().AsPagination(page??1); returnView("Index",ScaffoldView.Data().With(items)); }
publicoverrideActionResultNew() { Titem=newT { Position=OrderableService.NextPosition }; returnView("Edit",(object)BuildEditViewData().With(item)); }
[UnitOfWork] publicvirtualActionResultMoveUp(intid,int?page) { OrderableService.MoveItemAtPosition(id).UpOne(); returnRedirectToAction("Index"); } [UnitOfWork] publicvirtualActionResultMoveDown(intid,int?page) { OrderableService.MoveItemAtPosition(id).DownOne(); returnRedirectToAction("Index"); } }
复制代码
注:IOrderableService的实现相对复杂一些,具体内容详见Suteki.Common\Services\OrderableService.cs.
按说有了这些功能之后,只要在相应的子类中直接继承使用就可以了,但在Suteki.Shop项目中 作者又对OrderableScaffoldController进行了一个“继承式”扩展,提供了与前面所说的那个“ ControllerBase"相似的方法定义,如下:
复制代码 [Authenticate,CopyMessageFromTempDataToViewData] publicabstractclassShopScaffoldController:OrderableScaffoldController,IProvidesBaseServicewhereT:class,IOrderable,new() { privateIBaseControllerServicebaseControllerService;
/// ///Suppliesservicesandconfigurationtoallcontrollers /// publicIBaseControllerServiceBaseControllerService { get{returnbaseControllerService;} set { baseControllerService=value; ViewData["Title"]="{0}{1}".With( baseControllerService.ShopName, GetControllerName()); } }
publicvirtualstringGetControllerName() { return"-{0}".With(GetType().Name.Replace("Controller","")); } } 复制代码
而ShopScaffoldController这个抽象类有三个子类,如下图:
因为这三个Controller的功能需求相似,而相应的Action实现也在基类“ScaffoldController” 中实现,所以相应的类代码基本上就没有什么了。只不过在这几个子类中都被绑定了UnitOfWork过滤 器(UnitOfWorkFilter),其代码如下Suteki.Common\Filters\UnitOfWorkAttribute.cs:
复制代码 publicclassUnitOfWorkAttribute:FilterUsingAttribute { publicUnitOfWorkAttribute():base(typeof(UnitOfWorkFilter)) {} }
publicclassUnitOfWorkFilter:IActionFilter { privatereadonlyIDataContextProviderprovider;
publicUnitOfWorkFilter(IDataContextProviderprovider) { this.provider=provider; }
publicvoidOnActionExecuting(ActionExecutingContextfilterContext) {}
publicvoidOnActionExecuted(ActionExecutedContextfilterContext) { varcontext=provider.DataContext;
if(filterContext.Controller.ViewData.ModelState.IsValid) { context.SubmitChanges(); } } } 复制代码
其核心功能就是在对用户提交的数据进行有效验证后调用DataContext的SubmitChanges()方法 (注:该逻辑被放在了OnActionExecuted方法中实现)来保存修改,这种做法在以往的MVC示例子没有 看到过,呵呵,不过这种做法还有待研究。 |
|