65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 中的可扩展点:ControllerFactory 类处理 Controller 实例化

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (2投票s)

2014 年 7 月 23 日

CPOL

5分钟阅读

viewsIcon

10905

这是 ASP.NET MVC 可扩展点系列文章。在本系列中,我们将讨论 MVC 框架的各种可扩展点。

引言

这是 ASP.NET MVC 架构系列可扩展点的第一篇文章。在本系列中,我将描述 ASP.NET MVC 框架的各种可扩展点。我们知道 MVC 框架在微软的 Web 技术中非常流行,它提供了真正关注点分离的理念。MVC 框架的设计理念是,每个软件组件都是零散的,并且高度解耦。因此,可以灵活地添加和移除这些组件。同样,这些零散的组件也具有高度的可扩展性。例如,如果开发人员认为某个模块或组件的默认设置不适用于当前场景,她始终可以使用扩展来重新定义它,使其符合要求。ASP.NET 的一些可扩展点包括路由、Controller 创建时、View 创建时等等。

在本文中,我计划讨论 Controller 创建时的可扩展性,以及如何在 Controller 初始化时编写和注入自己的逻辑,并提供示例。那么,让我们开始理解 Controller 的创建机制。

IControllerFactory 接口

该接口位于 Controller 初始化机制的顶层。它位于类库的 System.Web.Mvc 命名空间中。它包含三个方法。CreateController 方法负责创建 Controller 的实例,它接受两个参数,第一个参数是请求上下文,其中包含当前 HTTP 请求的所有信息,包括用户的上下文,例如用户是否已认证以及用户名。第二个参数是一个字符串类型,用于指定 Controller 名称。它将获取当前 HTTP 请求想要访问的 Controller 名称。

namespace System.Web.Mvc
{
    // Summary:
    //     Defines the methods that are required for a controller factory.
    public interface IControllerFactory
    {
        // Summary:
        //     Creates the specified controller by using the specified request context.
        //
        // Parameters:
        //   requestContext:
        //     The request context.
        //
        //   controllerName:
        //     The name of the controller.
        //
        // Returns:
        //     The controller.
        IController CreateController(RequestContext requestContext, string controllerName);
        //
        // Summary:
        //     Gets the controller's session behavior.
        //
        // Parameters:
        //   requestContext:
        //     The request context.
        //
        //   controllerName:
        //     The name of the controller whose session behavior you want to get.
        //
        // Returns:
        //     The controller's session behavior.
        SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
        //
        // Summary:
        //     Releases the specified controller.
        //
        // Parameters:
        //   controller:
        //     The controller.
        void ReleaseController(IController controller);
    }
}

第二个方法是 GetControllerSessionBehavior() ,该方法设置环境的会话行为。第三个方法是 ReleaseController()。顾名思义,该方法用于在使用后释放 Controller 实例。该接口在 MVC 框架库的 ControllerFactory 类中实现。现在,如果我们的应用程序需要更多地控制 Controller 的实例化机制并希望以自己的方式实现这些方法,那么大门总是为我们敞开的。我们可以通过实现自己的 Factory 类来完成这些方法,并将该类注入到执行管道中。

从 IController 实现我们自己的 Controller Factory

现在,我们将以自己的方式实现 IControllerFactory 接口。在本例中,我们创建了一个名为“MyControllerFactory”的类并实现了 IControllerFactory。实现的目的是非常简单的;我们只是想激活一个 Controller。让我们来理解 CreateController 方法。我们从 *Web.config* 文件中获取程序集中的 Controller 位置,因为我们将使用称为反射的过程,然后我们调用 Activator 类的 CreateInstance() 方法。该方法接受 Controller 类型,即 Controller 类的类型。就这么结束。

public class MyControllerFactory : IControllerFactory
    {
        public IController CreateController
        (System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            string controllerType = string.Empty;
            IController controller = null;

            // Read Controller Class & Assembly Name from Web.Config
            controllerType = ConfigurationManager.AppSettings[controllerName];

            if (controllerType == null)
                throw new ConfigurationErrorsException
                ("Assembly not configured for controller " + controllerName);
            // Create Controller Instance
            controller = Activator.CreateInstance(Type.GetType(controllerType)) as IController;
            return controller;

        }
        public void ReleaseController(IController controller)
        {
            //This is a sample implementation
            //If pooling is used to write code to return the object to pool
            if (controller is IDisposable)
            {
                (controller as IDisposable).Dispose();
            }
            controller = null;
        }
        public SessionStateBehavior GetControllerSessionBehavior
        (RequestContext requestContext, string controllerName)
        {
            return SessionStateBehavior.Default;
        }
    }

如前所述,ReleaseController() 函数被实现为释放 Controller 实例。这里是实现。我们将 SessionStateBehaviour 属性保留为默认值。还有其他几个选项,超出了我们的范围。现在,我们将在 *web.config* 文件中定义程序集中 Controller 的位置。只需在 *web.config* 文件中添加一项。值部分就是 Controller 的名称加上命名空间。

    <add key="Home" value="Authentication.Controllers.HomeController" />

下一个要点是我们 Controller Factory 类的注册。我们必须注册该类,以便将我们的执行逻辑注入到 MVC 管道中。只需将以下行添加到 *Global.asax* 页面的 Application_Start() 事件中。

ControllerBuilder.Current.SetControllerFactory
(typeof(Authentication.Controllers.MyControllerFactory));

好了,我们几乎完成了,现在只需在应用程序中定义以下 Controller。一旦您运行应用程序并想要访问 Home Controller,您就会看到执行流程已经通过了我们的自定义 Controller Factory 类。这是 Home Controller 的实现。

 public class HomeController : Controller
       {
        public ActionResult Index()
        {
            return View();
        }
    }

从 Controller Factory 注入依赖项

好的,我们已经看到了在 Controller 创建时注入自定义逻辑,现在我们将实现一些实际场景,我们可以在其中实现和使用自定义 Controller Factory。让我们考虑 Controller 中的依赖项。如果 Controller 中存在依赖项,并且我们使用构造函数注入来通过传递为构造函数参数来解析依赖项,那么我们可能需要控制 Factory 类。我们知道 Controller 类的一个默认标准是无参构造函数,但在下面的示例中,我们通过构造函数注入来解析 Home Controller 的依赖项,为此,我们需要在 Home Controller 实例化时传递依赖对象。为了解决这个问题,我们将实现自己的 Controller Factory 并将其注入到执行管道中。请看下面的代码。

public class LogService
    {
        public void CallLog()
        {
            //LOG activity goes here
        }
    }

    public class HomeController : Controller
    {
        LogService log = null;
        public HomeController(LogService _serviceTemp)
        {
            log = _serviceTemp;
        }

        public ActionResult Index()
        {

            return View();
        }
    }

在代码中,我们看到 Home Controller 依赖于 LogService 对象,并且我们必须传递 LogService 类的实例。因此,让我们像下面这样修改 MyControllerFactory 类的 CreateController() 方法。

 public IController CreateController
(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            string controllerType = string.Empty;
            IController controller ;

            // Read Controller Class & Assembly Name from Web.Config.
            controllerType = ConfigurationManager.AppSettings[controllerName];

            //If not defined in web.config
            if (controllerType == null)
                throw new ConfigurationErrorsException("Controller not found " + controllerName);

            // Create Controller Instance using Activator class and inject reference of dependent object
            controller = Activator.CreateInstance
            (Type.GetType(controllerType),new HomeController(new LogService())) as IController;
            return controller;
        }

我们看到 LogService 对象作为 HomeController() 构造函数的参数传递,当我们想要访问 Home Controller 时,我们将看到依赖对象已在 Home Controller 的构造函数中填充。

尽管此示例仅用于演示工作流程,但在实际情况中,我们应该使用 IoC 容器来解析依赖项。这里我讨论的是 MVC Controller 中的依赖注入。

在 Controller Factory 中检查身份验证

这是我们可以实现自定义 Controller Factory 的另一个实际场景。Controller Factory 的基本目的是检查当前 HTTP 请求是否是经过身份验证的请求。如果请求是经过身份验证的请求,它将被重定向到适当的 Controller,如果请求未经身份验证,它将被重定向到 Notauthenticated Controller。这是简单的实现。

public IController CreateController
(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            string controllerType = string.Empty;
            IController controller ;

            // Read Controller Class & Assembly Name from Web.Config.
            controllerType = ConfigurationManager.AppSettings[controllerName];

            //If not defined in web.config
            if (controllerType == null)
                throw new ConfigurationErrorsException("Controller not found " + controllerName);

            if(requestContext.HttpContext.User.Identity.IsAuthenticated)
                controller = Activator.CreateInstance(Type.GetType(controllerType)) as IController;
            else
                controller = Activator.CreateInstance(typeof(NotAuthenticatedController)) as IController;
            return controller;
        }

这是最终的输出。

由于检查是在 Controller 创建的根级别执行的,因此它对于执行每个 Controller 都需要的逻辑非常有用,例如依赖注入或安全检查,如果所有 Controller 或一组 Controller 都需要这些逻辑。

总结

希望您已理解使用自定义 Controller Factory 类进行 Controller 初始化的概念。在下一篇文章中,我们将讨论 MVC 框架的另一个可扩展点。

© . All rights reserved.