在Suteki.Shop中对于Filter的使用上提供了两种方式,一种是从FilterAttribute
(抽象类属性)以及接口 IActionFilter和 IResultFilter中继承并实现。另一种是我们经
常提到的从ActionFilterAttribute 上继承方式来实现自己的ActionFilter。首先看一下
第一种,同时它也是该项目中被Action广泛使用的方式, 下面是类图:
当然图中最核心的当属FilterUsingAttribute,它同时继承了 FilterAttribute类和
IAuthorizationFilter, IActionFilter, IResultFilter这三个接口,所以其所实现的功能
与MVC中所定义的ActionFilterAttribute如出一辙。同时,下面是其核心代码:
{
private readonly Type filterType;
private object instantiatedFilter;
public FilterUsingAttribute(Type filterType)
{
if ( ! IsFilterType(filterType))
{
throw new InvalidOperationException( " Type '{0}' is not valid within the FilterUsing
attribute as it is not a filter type. " .With(filterType.Name));
}
this .filterType = filterType;
}
private bool IsFilterType(Type type)
{
return typeof (IAuthorizationFilter).IsAssignableFrom(type)
|| typeof (IActionFilter).IsAssignableFrom(type)
|| typeof (IResultFilter).IsAssignableFrom(type);
}
public Type FilterType
{
get { return filterType; }
}
private T GetFilter < T > () where T : class
{
if (instantiatedFilter == null )
{
instantiatedFilter = ServiceLocator.Current.GetInstance(filterType);
}
return instantiatedFilter as T;
}
private void ExecuteFilterWhenItIs < TFilter > (Action < TFilter > action) where TFilter : class
{
var filter = GetFilter < TFilter > ();
if (filter != null )
{
action(filter);
}
}
public void OnAuthorization(AuthorizationContext filterContext)
{
ExecuteFilterWhenItIs < IAuthorizationFilter > (f => f.OnAuthorization(filterContext));
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
ExecuteFilterWhenItIs < IActionFilter > (f => f.OnActionExecuting(filterContext));
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
ExecuteFilterWhenItIs < IActionFilter > (f => f.OnActionExecuted(filterContext));
}
public void OnResultExecuting(ResultExecutingContext filterContext)
{
ExecuteFilterWhenItIs < IResultFilter > (f => f.OnResultExecuting(filterContext));
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
ExecuteFilterWhenItIs < IResultFilter > (f => f.OnResultExecuted(filterContext));
}
}
在上面的OnAction..和OnResult..事件中,都调用了ExecuteFilterWhenItIs这个泛型方法,
而该方法的作用是对泛型约束中使用到的相应IActionFilter进行操作,而获取相应的Filter实例
的工作就交给了GetFilter<T>()方法,因为该方法使用IOC方式将filterType以服务组件的方式进
行创建,所以我们会看到在ContainerBuilder(Suteki.Shop/ContainerBuilder.cs)中有如下代
码,注意最后一行:
Component.For < IUnitOfWorkManager > ().ImplementedBy < LinqToSqlUnitOfWorkManager > ().LifeStyle.Transient,
Component.For < IFormsAuthentication > ().ImplementedBy < FormsAuthenticationWrapper > (),
Component.For < IServiceLocator > ().Instance( new WindsorServiceLocator(container)),
看来其最终会使用Castle框架所提供的IOC功能。
其实理解上面代码并不难,就是Suteki.Shop以自己实现的FilterUsingAttribute代替了MVC
自己的 ActionFilterAttribute, (当然它做的并不彻底,大家会在接下来的内容中看到)。当然有
了FilterUsingAttribute之后,Suteki.Shop并没有直接就去在Action中直接使用它,而是以它派
生出了几个Filter属性:
UnitOfWorkAttribute:项目中大部分Action使用
AuthenticateAttribute:用户信息认证
LoadUsingAttribute
其中UnitOfWorkAttribute和AuthenticateAttribute被用的最多,下面就分别加以介绍说明。
首先是UnitOfWorkAttribute,其构造方法声明中将UnitOfWorkFilter作为其基类方法的构
造类型,如下:
{
public UnitOfWorkAttribute() : base ( typeof (UnitOfWorkFilter))
{
}
}
public class UnitOfWorkFilter : IActionFilter
{
private readonly IDataContextProvider provider;
public UnitOfWorkFilter(IDataContextProvider provider)
{
this .provider = provider;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
var context = provider.DataContext;
if (filterContext.Controller.ViewData.ModelState.IsValid)
{
context.SubmitChanges();
}
}
}
其要实现的功能主要是对用户所做的数据操作进行判断,如果没有发生异常:
ModelState.IsValid为True时,则提交所做的修改到数据库中。
接下来再看一个AuthenticateAttribute,前面说过,其所实现的功能就是对当前
用户身份进行验证,其核心代码如下,因为内容比较简单,大家一看便知。
{
public AuthenticateAttribute() : base ( typeof (AuthenticateFilter))
{
Order = 0 ;
}
}
public class AuthenticateFilter : IAuthorizationFilter
{
private IRepository < User > userRepository;
private IFormsAuthentication formsAuth;
public AuthenticateFilter(IRepository < User > userRepository, IFormsAuthentication formsAuth)
{
this .userRepository = userRepository;
this .formsAuth = formsAuth;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var context = filterContext.HttpContext;
if (context.User != null && context.User.Identity.IsAuthenticated)
{
var email = context.User.Identity.Name;
var user = userRepository.GetAll().WhereEmailIs(email);
if (user == null )
{
formsAuth.SignOut();
}
else
{
AuthenticateAs(context, user);
return ;
}
}
AuthenticateAs(context, User.Guest);
}
private void AuthenticateAs(HttpContextBase context, User user)
{
Thread.CurrentPrincipal = context.User = user;
}
}
当然在本文开篇说过,Suteki.Shop也使用了我们经常用到的方式,即从ActionFilterAttribute
继承实现自己的ActionFilter,比如说CopyMessageFromTempDataToViewData(Suteki.Shop
/Filters/CopyMessageFromTempDataToViewData.cs),从字面可以看出,其要实现的功能就
是把临时数据复制到ViewData中,以便于前台视图显示,下面是其类图:
其实现代码如下:
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResult;
if (result != null && filterContext.Controller.TempData.ContainsKey( " message " ))
{
var model = result.ViewData.Model as ShopViewData;
if (model != null && string .IsNullOrEmpty(model.Message))
{
model.Message = filterContext.Controller.TempData[ " message " ] as string ;
}
}
}
}
其实看到这里,我感觉Suteki.Shop对于ActionFilter的使用还有待商榷,必定 MVC中的 Filter
是一种耗时的操作,对于程序的运行速度和执行效率来说都是一个考验。这其实也能部分解释为什么我
在本地运行Suteki.Shop时速度会比较慢。
这里不妨开句玩笑,Suteki.Shop开发者似乎得到ActionFilter强迫症,因为我感觉一个项目中一
个Action绑定的Filter最好别超过2个,否则必然会影响程序运行效率,尽管Suteki.Shop基本上控制
在了2个左右,但其还是运行速度偏慢。当然这里我并没有做到具体的测试,只是部分猜测,不过有兴
趣的朋友不妨测试一下,看看结果如何,相信会见分晓。
好了,今天的内容就先到这里了。
原文链接:http://www.cnblogs.com/daizhj/archive/2009/05/14/1453885.html
作者: daizhj,代震军,LaoD
Tags: mvc,Suteki.Shop
网址: http://daizhj.cnblogs.com/