配色: 字号:
Asp.net MVC 示例项目Suteki.Shop分析之---Controller
2016-09-18 | 阅:  转:  |  分享 
  
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示例子没有
看到过,呵呵,不过这种做法还有待研究。
献花(0)
+1
(本文系thedust79首藏)