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个左右,但其还是运行速度偏慢。当然这里我并没有做到具体的测试,只是部分猜测,不过有兴趣的朋友不妨测试一下,看看结果如何,相信会见分晓。
 
         好了,今天的内容就先到这里了。
       
    

 

你可能感兴趣的:(mvc,职场,休闲,suteki.shop)