ASP MVC: когда вызывается IController Dispose ()?

83

Я прохожу большой рефакторинг / настройку скорости одного из моих больших приложений MVC. Он был развернут в производственной среде в течение нескольких месяцев, и я начинал получать таймауты на ожидание подключений в пуле подключений. Я отследил проблему до того, что соединения не удаляются должным образом.

В свете этого я внес следующие изменения в свой базовый контроллер:

public class MyBaseController : Controller
{
    private ConfigurationManager configManager;  // Manages the data context.

    public MyBaseController()
    {
         configManager = new ConfigurationManager();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (this.configManager != null)
            {
                this.configManager.Dispose();
                this.configManager = null;
            }
        }

        base.Dispose(disposing);
    }
}

Теперь у меня два вопроса:

  1. Я ввожу состояние гонки? Поскольку он configManagerуправляет тем, DataContextчто предоставляет IQueryable<>параметры представлениям, мне нужно убедиться, что Dispose()он не будет вызван на контроллере до того, как представление завершит рендеринг.
  2. Обращается ли среда MVC Dispose()к контроллеру до или после визуализации представления? Или структура MVC оставляет это на усмотрение GarbageCollector?
Джон Гитцен
источник
2
Я ооочень жду ответа на этот вопрос! БОЛЬШОЙ вопрос!
Дэниел Эллиотт,
Не глядя на другой код (ваш или ASP.NET MVC ..), почему именно вам нужно обнулить configManager? Это хоть как-то помогает? Тщательно подумайте, прежде чем кто-нибудь из вас "DUH" меня ..
Андрей Рыня
Я имею в виду, что в таком общем случае условие гонки может быть легко удалено таким образом. В этом конкретном случае я сомневаюсь, что экземпляр контроллера будет использоваться более чем одним потоком, и поэтому нет никакого риска состояния гонки.
Андрей Рыня
1
@Andrei: Это просто защитный код. Это не позволяет мне дважды удалить соединение с базой данных, если мой метод удаления вызывается дважды.
Джон Гитцен,
1
@Andrei: Ну, на мой взгляд, «Игнорирование» и «В любом случае вызов Dispose для дочерних объектов» совершенно разные. Отсюда и проверка.
Джон Гитцен,

Ответы:

70

Dispose всегда вызывается после рендеринга представления .

Представление отображается при вызове ActionResult.ExecuteResult. Это называется (косвенно) by ControllerActionInvoker.InvokeAction, который, в свою очередь, вызывает ControllerBase.ExecuteCore.

Поскольку при рендеринге представления контроллер находится в стеке вызовов, его нельзя удалить.

Крэйг Стунц
источник
Отлично, у вас есть документация? Я просто хочу быть уверенным.
Джон Гитцен, 04
Большой! Было бы здорово найти документ, объясняющий это. Но развернутый ответ действительно утешил. Код вообще лучший документ. : D
CSA
37

Просто чтобы расширить ответ Крейга Стунца :

ControllerFactory обрабатывает, когда контроллер удаляется. При реализации интерфейса IControllerFactory одним из методов, который необходимо реализовать, является ReleaseController.

Я не уверен, какой ControllerFactory вы используете, независимо от того, свернули ли вы свой собственный, но в Reflector, глядя на DefaultControllerFactory, метод ReleaseController реализован следующим образом:

public virtual void ReleaseController(IController controller)
{
    IDisposable disposable = controller as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

Ссылка на IController передается, если этот контроллер реализует IDisposable, то вызывается метод Dispose этого контроллера. Итак, если у вас есть что-то, что вам нужно удалить после завершения запроса, то есть после визуализации представления. Наследуйте IDisposable и поместите свою логику в метод Dispose, чтобы освободить все ресурсы.

Метод ReleaseController вызывается System.Web.Mvc.MvcHandler, который обрабатывает запрос и реализует IHttpHandler. ProcessRequest принимает предоставленный ему HttpContext и запускает процесс поиска контроллера для обработки запроса, вызывая реализованный ControllerFactory. Если вы посмотрите на метод ProcessRequest, вы увидите блок finally, который вызывает ReleaseController ControllerFactory. Это вызывается только тогда, когда контроллер возвратил ViewResult.

Дейл Рэган
источник
Отличный ответ. Я не мог понять, почему прямой экземпляр объекта Controller не позволяет мне вызвать для него Dispose (), но похоже, что мне нужно создать его новый экземпляр с помощью интерфейса IDisposable. Это сработало для меня!
MegaMatt,
Так ... HttpContextэто мужчина? Теперь я действительно запутался.
Chef_Code