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

Я прохожу большой рефакторинг / настройку скорости одного из моих больших приложений 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?

person John Gietzen    schedule 04.09.2009    source источник
comment
Я ооочень жду ответа на этот вопрос! ОТЛИЧНЫЙ вопрос!   -  person Daniel Elliott    schedule 04.09.2009
comment
Не глядя на другой код (ваш или ASP.NET MVC ..), почему именно вам нужно обнулить configManager? Это хоть чему-нибудь поможет? Тщательно подумайте перед любым из вас, ДУХ Я ..   -  person Andrei Rînea    schedule 11.10.2010
comment
Я имею в виду, что в таком общем случае условие гонки может быть легко удалено таким образом. В этом конкретном случае я сомневаюсь, что экземпляр контроллера будет использоваться более чем одним потоком, и поэтому нет никакого риска состояния гонки.   -  person Andrei Rînea    schedule 11.10.2010
comment
@Andrei: Это просто защитный код. Это не позволяет мне дважды удалить соединение с базой данных, если мой метод удаления вызывается дважды.   -  person John Gietzen    schedule 11.10.2010
comment
Хорошо, но метод Dispose, определенный в интерфейсе IDisposable, ясно говорит, что безопасно вызывать более одного раза. И я думаю, что все разработчики соблюдают это правило .. msdn.microsoft.com/en-us/library/ четко заявляет, что если метод Dispose объекта вызывается более одного раза, объект должен игнорировать все вызовы после первого.   -  person Andrei Rînea    schedule 12.10.2010
comment
@Andrei: Ну, на мой взгляд, игнорирование и вызов Dispose для дочерних объектов в любом случае совершенно разные. Отсюда и проверка.   -  person John Gietzen    schedule 12.10.2010


Ответы (2)


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

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

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

person Craig Stuntz    schedule 04.09.2009
comment
Отлично, у вас есть документация? Я просто хочу быть уверенным. - person John Gietzen; 04.09.2009
comment
Большой! Было бы здорово найти документ, объясняющий это. Но развернутый ответ действительно утешил. Код вообще лучший документ. : D - person CSA; 23.07.2013

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

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.

person Dale Ragan    schedule 04.09.2009
comment
Отличный ответ. Я не мог понять, почему прямой экземпляр объекта Controller не позволяет мне вызвать для него Dispose (), но похоже, что мне нужно создать его новый экземпляр с помощью интерфейса IDisposable. Это сработало для меня! - person MegaMatt; 13.09.2012
comment
Итак ... HttpContext мужчина? Теперь я действительно запутался. - person Chef_Code; 14.02.2016