• 将Asp.Net MVC应用程序的控制器定义在单独的程序集(类库)中


      一直以来都想把控制器的代码部署到单独的程序集里。昨天研究Asp.Net MVC的源代码,偶然发现有一个奇特的类“ControllerBuilder”,MSDN上的介绍相当简略,就一句话“表示一个类,该类负责动态生成控制器。”。小试了一把,竟然成功了!原来Asp.Net MVC程序的Controllers不是只能定义在程序根目录的Controllers文件夹下面的。

      要实现控制器单独部署的重点在于“ControllerBuilder”的“SetControllerFactory”方法,该方法的一个重载要求一个类型为“IControllerFactory”的参数。所以,实现“IControllerFactory”是前提。

      “IControllerFactory”,顾名思义,就是控制器工厂类要继承的接口。如果实现了该接口,就表示我们可以按自已的方法来替换MVC框架中搜索控制器的操作。该接口要求实现三个方法,它们是“CreateController、GetControllerSessionBehavior、ReleaseController”。

      1、实现“CreateController”:这个方法很容易理解,它返回“IController”接口。因为“ControllerBase”实现了该接口,当然我们定义的所有控制器类型也必然实现了这个接口。所以只要返回控制器的实例就可以了。方法传入“RequestContext requestContext,  string controllerName” 两个参数,这就需要先根据controllerName生成一个没有请求上下文对象的空的控制器,再为这个控制器指定“ControllerContext”,然后返回这个控制器实例就搞定了。

      2、实现“GetControllerSessionBehavior”:这个方法比较难懂,它的返回值类型“SessionStateBehavior”是一个枚举类型,表示的是请求对会话状态的支持类型。这个东西从哪儿得到呢?方法传入的参数还是“RequestContext requestContext, string controllerName”这两个,可见,这个返回的值还是得从实际执行请求的那个控制器那里得到。这时候我想到都可以对控制器使用“SessionState”标记类,它正好是“Behavior”属性,就它了。获取到“controllerName”对应的控制器类型中定义的“SessionState”属性的实例,返回它的“Behavior”就OK了;当然,有可能没有给控制器加那个标记,那就要返回“SessionStateBehavior.Default”了。

      3、实现“ReleaseController”:这个太容易了,判断一下传入的参数有没有实现“IDisposable”,如果有,就调用它的“Dispose()”方法就行了。

      上面说那么多,上点实际的东西,来看看我的代码是怎么写的。

        /// <summary>
        /// 控制器工厂类
        /// </summary>
        public class ControllerFactory : IControllerFactory
        {
            readonly string _AssemblyName;
            /// <summary>
            /// 获取控制器所在的程序集名称
            /// </summary>
            public string AssemblyName 
            {
                get { return _AssemblyName; }
            }
    
            readonly string _DefaultNameSpace;
            /// <summary>
            /// 获取控制器的默认名称空间
            /// </summary>
            public string DefaultNameSpace
            {
                get { return _DefaultNameSpace; }
            }
    
            Assembly _ControllerAssembly;
            /// <summary>
            /// 获取控制器所在的程序集的Assembly实例
            /// </summary>
            Assembly ControllerAssembly
            {
                get
                {
                    if (_ControllerAssembly == null)
                    {
                        _ControllerAssembly = Assembly.Load(AssemblyName);
                    }
                    return _ControllerAssembly;
                }
            }
    
            public ControllerFactory(string assemblyName)
            {
                _AssemblyName = assemblyName;   
            }
    
            public ControllerFactory(string assemblyName, string defaultNameSpace)
            {
                _AssemblyName = assemblyName;
                _DefaultNameSpace = defaultNameSpace;
            }
    
            /// <summary>
            /// 获取控制器类的全名
            /// </summary>
            /// <param name="controllerName"></param>
            /// <returns></returns>
            string GetControllerFullName(string controllerName)
            {
                return string.Format(
                    "{0}.{1}Controller", string.IsNullOrEmpty(DefaultNameSpace) ? AssemblyName : DefaultNameSpace, controllerName);
            }
    
            public IController CreateController(RequestContext requestContext, string controllerName)
            {
                var controller = 
                    ControllerAssembly.CreateInstance(GetControllerFullName(controllerName)) as Controller;
                if (controller != null)
                {
                    if (controller.ControllerContext == null)
                    {
                        controller.ControllerContext = new ControllerContext(requestContext, controller);
                    }
                    else
                    {
                        controller.ControllerContext.RequestContext = requestContext;
                    }
                    return controller as IController;
                }
                return null;
            }
    
            public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
            {
                var controllerType = ControllerAssembly.GetType(GetControllerFullName(controllerName), true, true);
                var sessionStateAttr = 
                    Attribute.GetCustomAttribute(controllerType ,typeof(SessionStateAttribute), false) as SessionStateAttribute;
                return sessionStateAttr == null ? SessionStateBehavior.Default : sessionStateAttr.Behavior;
            }
    
            public void ReleaseController(IController controller)
            {
                IDisposable disposable = controller as IDisposable;
                if (disposable != null)
                {
                    disposable.Dispose();
                }
            }
        }
    View Code

      现在有了“IControllerFactory”,需要在注册路由规则前利用“ControllerBuilder”指定使用刚才自定义的工厂类作为创建当前MVC应用程序的工厂类。在Global.asax的“RegisterRoutes()”方法里面的第一句前加上这样一句:“ControllerBuilder.Current.SetControllerFactory(new ControllerFactory("APP.Controlers"));”,这样就行了,“APP.Controlers”是一个独立程序集的名称,因为我是通过返回来获取Controller的实例,必须要知道控制器在哪个和程序集里。当然,总是有更好的方法的。

      F5运行,你会发现MVC不会再从你Controllers目录下面找控制器了……

  • 相关阅读:
    矩阵补全(Matrix Completion)和缺失值预处理
    【机器学习基础】对 softmax 和 cross-entropy 求导
    Mendeley使用小技巧
    [Active Learning] Multi-Criteria-based Active Learning
    回归树(Regression Tree)
    Typora + Mathpix Snip,相见恨晚的神器
    【机器学习之数学】02 梯度下降法、最速下降法、牛顿法、共轭方向法、拟牛顿法
    【机器学习之数学】01 导数、偏导数、方向导数、梯度
    “卷积神经网络(Convolutional Neural Network,CNN)”之问
    一篇带你完全掌握线程的博客
  • 原文地址:https://www.cnblogs.com/shalves/p/Defined_The_MVC_S_Controllers_In_A_Separate_Assembly.html
Copyright © 2020-2023  润新知