Asp.net MVC 示例项目"Suteki.Shop"分析之---Filter

      在Suteki.Shop中对于Filter的使用上提供了两种方式,一种是从FilterAttribute
(抽象类属性)以及接口 IActionFilterIResultFilter中继承并实现。另一种是我们经
常提到的从ActionFilterAttribute 上继承方式来实现自己的ActionFilter。首先看一下
第一种,同时它也是该项目中被Action广泛使用的方式, 下面是类图:

     

     当然图中最核心的当属FilterUsingAttribute,它同时继承了 FilterAttribute类和
IAuthorizationFilter, IActionFilter, IResultFilter这三个接口,所以其所实现的功能
与MVC中所定义的ActionFilterAttribute如出一辙。同时,下面是其核心代码:
    

public class FilterUsingAttribute : FilterAttribute, IAuthorizationFilter, IActionFilter, IResultFilter
{
        
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)中有如下代
码,注意最后一行:  

container.Register(
    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 class UnitOfWorkAttribute : FilterUsingAttribute
{
        
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 class AuthenticateAttribute : FilterUsingAttribute
{
        
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 class CopyMessageFromTempDataToViewData : ActionFilterAttribute
{
        
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/


 

posted @ 2009-05-14 08:10  代震军  阅读(5447)  评论(21编辑  收藏  举报