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: Це лише трохи захисного кодування. Це заважає мені утилізувати підключення до бази даних двічі, якщо мій метод dispose буде викликаний двічі.
Джон Гітцен,

1
@Andrei: Ну, на мою думку, "Ігнорування" та "Виклик утилізації дочірніх предметів у будь-якому випадку" абсолютно різні. Звідси перевірка.
Джон Гітцен,

Відповіді:


70

Dispose викликається після подання подання, завжди .

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

Оскільки контролер знаходиться у стеці викликів, коли подається подання, тоді він не може бути утилізований.


Чудово, у вас є документація? Я просто хочу бути впевненим.
Джон Гітцен,

Чудово! Було б чудово знайти документа, який би це пояснив. Але розширена відповідь була справді втішною. Код - це взагалі кращий документ. : 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, ви побачите нарешті блок, який викликає ReleaseController ControllerFactory. Це викликається лише тоді, коли контролер повернув ViewResult.


Чудова відповідь. Я не міг зрозуміти, чому прямий екземпляр об’єкта Controller не дозволяє мені викликати Dispose () на ньому, але схоже, мені потрібно створити його новий екземпляр за допомогою інтерфейсу IDisposable. Це спрацювало для мене!
MegaMatt

Отже ... HttpContextце самець? Зараз я справді розгублений.
Chef_Code
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.