Orchard源碼分析(7.2):Controller相關

概述
默認狀況下,ASP.NET MVC內置的DefaultControllerFactory負責Controller實例的建立。Orchard定義了一個繼承自DefaultControllerFactory類的Orchard.Mvc.OrchardControllerFactory類並在OrchardStarter類中進行註冊:
         // 如下代碼來在Orchard.Environment.OrchardStarter類
          ControllerBuilder .Current.SetControllerFactory( new   OrchardControllerFactory ());
OrchardControllerFactory做用是能從Shell生命週期的Autofac"子容器"中解析(Resolver)Controller類型,再根據類型解析Controller實例。這裏就引出這些問題:Controller類型在何時被註冊到Autofac容器中,Controller的類型如何被解析出來,Controller的實例如何被解析出來。
 
1、Controller註冊
在激活Shell以前,會建立Shell的上下文對象ShellContext,在這個過程當中會將Shell須要用到的Controller註冊到Shell做用域的Autofac容器中:
                     // 如下代碼來在Orchard.Environment.ShellBuilders.ShellContainerFactory類的CreateContainer方法
                      foreach   ( var   item   in   blueprint.Controllers) {
                          var   serviceKeyName = (item.AreaName +   "/"   + item.ControllerName).ToLowerInvariant();
                          var   serviceKeyType = item.Type;
                        RegisterType(builder, item)
                            .EnableDynamicProxy(dynamicProxyContext)
                            .Keyed<   IController >(serviceKeyName)
                            .Keyed<   IController >(serviceKeyType)
                            .WithMetadata(   "ControllerType" , item.Type)
                            .InstancePerDependency()
                            .OnActivating(e =>s {
                                  // necessary to inject custom filters dynamically
                                  // see FilterResolvingActionInvoker
                                  var   controller = e.Instance   as   Controller ;
                                  if   (controller !=   null   )
                                    controller.ActionInvoker = (   IActionInvoker )e.Context.ResolveService( new   TypedService ( typeof   ( IActionInvoker )));
                            });
                    }
 
                      foreach   ( var   item   in   blueprint.HttpControllers) {
                          var   serviceKeyName = (item.AreaName +   "/"   + item.ControllerName).ToLowerInvariant();
                          var   serviceKeyType = item.Type;
                        RegisterType(builder, item)
                            .EnableDynamicProxy(dynamicProxyContext)
                            .Keyed<   IHttpController >(serviceKeyName)
                            .Keyed<   IHttpController >(serviceKeyType)
                            .WithMetadata(   "ControllerType" , item.Type)
                            .InstancePerDependency();
                    }
 
          // 如下代碼來在Orchard.Environment.ShellBuilders.ShellContainerFactory類
        private   IRegistrationBuilder < object   ,   ConcreteReflectionActivatorData ,   SingleRegistrationStyle > RegisterType( ContainerBuilder   builder,   ShellBlueprintItem   item) {
              return   builder.RegisterType(item.Type)
                .WithProperty(   "Feature" , item.Feature)
                .WithMetadata(   "Feature" , item.Feature);
        }
2、ControllerFactory
既然Controller已經被註冊到了Autofac容器中,則很容易提取出來:
      // 如下代碼來在Orchard.Mvc.ControllerFactory類
     ///   <summary>
      ///   Overrides the default controller factory to resolve controllers using LoC, based their areas and names.
      ///   </summary>
      public   class   OrchardControllerFactory   :   DefaultControllerFactory   {
          ///   <summary>
          ///   Tries to resolve an instance for the controller associated with a given service key for the work context scope.
          ///   </summary>
          ///   <typeparam name="T">   The type of the controller. </typeparam>
          ///   <param name="workContext">   The work context. </param>
          ///   <param name="serviceKey">   The service key for the controller.   </param>
          ///   <param name="instance">   The controller instance. </param>
          ///   <returns>   True if the controller was resolved; false otherwise.   </returns>
          protected   bool   TryResolve<T>( WorkContext   workContext,   object   serviceKey,   out   T instance) {
              if   (workContext !=   null   && serviceKey !=   null ) {
                  var   key =   new   KeyedService (serviceKey,   typeof   (T));
                  object   value;
                  if   (workContext.Resolve< ILifetimeScope   >().TryResolveService(key,   out   value)) {
                    instance = (T) value;
                      return   true   ;
                }
            }
 
            instance =   default (T);
              return   false   ;
        }
 
          ///   <summary>
          ///   Returns the controller type based on the name of both the controller and area.
          ///   </summary>
          ///   <param name="requestContext">   The request context from where to fetch the route data containing the area. </param>
          ///   <param name="controllerName">   The controller name. </param>
          ///   <returns>   The controller type. </returns>
          ///   <example>   ControllerName: Item, Area: Containers would return the type for the ItemController class. </example>
          protected   override   Type   GetControllerType(   RequestContext   requestContext,   string   controllerName) {
              var   routeData = requestContext.RouteData;
 
              // Determine the area name for the request, and fall back to stock orchard controllers
              var   areaName = routeData.GetAreaName();
 
              // Service name pattern matches the identification strategy
              var   serviceKey = (areaName +   "/"   + controllerName).ToLowerInvariant();
 
              // Now that the request container is known - try to resolve the controller information
              Meta < Lazy   < IController >> info;
              var   workContext = requestContext.GetWorkContext();
              if   (TryResolve(workContext, serviceKey,   out   info)) {
                  return   ( Type   ) info.Metadata[ "ControllerType" ];
            }
 
              return   null   ;
        }
 
          ///   <summary>
          ///   Returns an instance of the controller.
          ///   </summary>
          ///   <param name="requestContext">   The request context from where to fetch the route data containing the area. </param>
          ///   <param name="controllerType">   The controller type. </param>
          ///   <returns>   An instance of the controller if it's type is registered; null if otherwise.   </returns>
          protected   override   IController   GetControllerInstance(   RequestContext   requestContext,   Type   controllerType) {
              IController   controller;
              var   workContext = requestContext.GetWorkContext();
              if   (TryResolve(workContext, controllerType,   out   controller)) {
                  return   controller;
            }
 
              // fail as appropriate for MVC's expectations
              return   base   .GetControllerInstance(requestContext, controllerType);
        }
    }
 
 
3、ActionInvoker
在成功建立Controller實例後,會將其ActionInvoker設置成Orchard.Mvc.FilterResolvingActionInvoker。
咱們知道,ASP.NET MVC中,咱們能夠將Filter以Attribute的形式定義在Controller或Action上。Orchard提供了一個Filter Provider機制,便可以將一些Filter定義在其餘地方,在使用Filter以前再提取出來。這方面比較簡單,直接看FilterResolvingActionInvoker的定義:
      // 如下代碼來在Orchard.Mvc.Filters.FilterResolvingActionInvoker類
     public   class   FilterResolvingActionInvoker   :   ControllerActionInvoker   {
          private   readonly   IEnumerable <   IFilterProvider > _filterProviders;
 
          public   FilterResolvingActionInvoker( IEnumerable   < IFilterProvider > filterProviders) {
            _filterProviders = filterProviders;
        }
 
          protected   override   FilterInfo   GetFilters( ControllerContext   controllerContext,   ActionDescriptor   actionDescriptor) {
              var   filters=   base   .GetFilters(controllerContext, actionDescriptor);
              foreach ( var   provider   in   _filterProviders) {
                provider.AddFilters(filters);
            }
              return   filters;
        }
    }
 
只要一個Filter實現了Orchard.Mvc.Filters.FilterProvider:FilterProvider抽象類,就能很是方便的將Filter應用到全部Action之上。另外,高級的應用能夠直接實現IFilterProvider接口,不過通常實現FilterProvider抽象類就好了。
 
相關類型:
Orchard.Mvc.OrchardControllerFactory:System.Web.Mvc.DefaultActionInvoker
Orchard.Mvc.Filters.FilterResolvingActionInvoker:System.Web.Mvc.ControllerActionInvoker
Orchard.Mvc.Filters.IFilterProvider
Orchard.Environment.ShellBuilders.ShellContainerFactory
Orchard.Environment.AutofacUtil.DynamicProxy2.DynamicProxyContext
相關文章
相關標籤/搜索