在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