ASP.NET MVC:每个请求都会创建一个控制器吗?

140

非常简单的问题:ASP.NET中的控制器是为每个HTTP请求创建的,还是在应用程序启动时创建并在整个请求期间重复使用?

控制器只会为特定的HTTP请求创建吗?

如果我的之前的假设是正确的,那么我可以依赖它吗?我想创建一个数据库上下文(Entity Framework),它仅在一个请求中存在。如果我将其作为在控制器构造函数中初始化的属性创建,那么新的上下文实例是否保证仅在每个请求中创建一次?


16
在你的构造函数中设置一个断点,看看你能发现什么... - Greg B
12
@Greg B:这是个好主意,但它不会告诉我它是否总是表现出这样的行为 - 如果情况改变,某个控制器会改变其行为,那么我可能会有一个非常难以发现的错误... - Rasto
4
@Todd Smith,请提供链接或至少完整名称。缩写“IoC”很难在谷歌上搜索到。谢谢。 - Rasto
2
@drasto IoC = 控制反转 http://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC - Bala R
4个回答

120

每个请求都会由ControllerFactory(默认情况下为DefaultControllerFactory)创建一个控制器。

http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultcontrollerfactory.aspx

请注意,Html.Action Html Helper将创建另一个控制器。
简短的版本是,ControllerActivator.Create会被调用(对于每个请求),以创建一个控制器(通过DependencyResolver或如果没有设置解析器,则通过Activator初始化新的控制器):
public IController Create(RequestContext requestContext, Type controllerType) 
{
    try 
    {
        return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
    }

更详细的版本如下(以下是来自 MvcHandler 源代码的代码):

protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
    SecurityUtil.ProcessInApplicationTrust(() =>
    {
        IController controller;
        IControllerFactory factory;
        ProcessRequestInit(httpContext, out controller, out factory);

        try
        {
            controller.Execute(RequestContext);
        }
        finally
        {
            factory.ReleaseController(controller);
        }
    });
}

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
    // non-relevant code
    // Instantiate the controller and call Execute
    factory = ControllerBuilder.GetControllerFactory();
    controller = factory.CreateController(RequestContext, controllerName);
    if (controller == null)
    {
        throw new InvalidOperationException(
            String.Format(
                CultureInfo.CurrentCulture,
                MvcResources.ControllerBuilder_FactoryReturnedNull,
                factory.GetType(),
                controllerName));
    }
}

以下是控制器工厂的代码:

public virtual IController CreateController(RequestContext requestContext, string controllerName) 
{
    Type controllerType = GetControllerType(requestContext, controllerName);
    IController controller = GetControllerInstance(requestContext, controllerType);
    return controller;
}

这基本上调用了这个:

protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) 
{
    return ControllerActivator.Create(requestContext, controllerType);
}

这个方法在ControllerActivator中被调用(这段代码尝试向DependencyResolver请求一个实例,或者直接使用Activator类):

public IController Create(RequestContext requestContext, Type controllerType) 
{
    try 
    {
        return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
    }

这可能属于太多的信息...但我想表明,您确实会为每个请求获得一个新的控制器。


@Daniel @drasto 这是引用链接 http://aspnet.codeplex.com/SourceControl/changeset/view/63930#266503 - Bala R

39

我为控制器创建了一个空构造函数,并在其中设置了断点。每当有新的请求时,它都会被触发。因此,我认为它是为每个请求创建的。


3
我希望你是对的,但我希望有更可靠的知识支持,而不仅仅是“在我尝试的所有情况下都有效”。如果由于某些原因它有时候不能按照那种方式工作,那就意味着存在一个错误。 - Rasto
8
不需要担心。每个请求都会实例化控制器。但是一些内存会被重复使用,不过你不必担心控制器状态(如果有的话),因为它会按预期初始化。但是,可能会出现多个控制器实例的情况。这种情况发生在视图调用控制器操作时(例如Html.RenderAction("action", "controller"))。 - Robert Koritnik

3

当执行特定控制器中的任何操作时,将创建控制器。

我的项目中,所有控制器都继承自ApplicationController,每次执行操作时,断点都会命中在ApplicationController中 - 不管它的"当前"控制器是什么。

我像这样在创建控制器时初始化我的代理(作为我的上下文):

    public IWidgetAgent widgetAgent { get; set; }

    public WidgetController()
    {
        if (widgetAgent == null)
        {
            widgetAgent = new WidgetAgent();
        }

    }

这显然不是你需要的 - 因为你提到你只想每次调用时有一个单独的实例。但这是一个检查每次发生的情况并确保您的上下文没有另一个实例存在的好地方。
希望这可以帮助你。

2

“citation needed” - 在链接的文件中找不到支持信息。谢谢 - Rasto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接