Подтверждение интерфейса IDisposable

#c# #autofac #idisposable

#c# #autofac #idisposable

Вопрос:

Мы подробно прочитали это:

https://autofaccn.readthedocs.io/en/latest/lifetime/disposal.html ?выделить =Удалить

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

У меня есть один DisposableClass, который реализует IDisposable, и один NotDisposableClass.

Учитывая следующий код:

 
        private IContainer _container;
        private ILifetimeScope _scope1;
        private ILifetimeScope _scope2;

        public Form1()
        {
            InitializeComponent();

            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<DisposableClass>();
            builder.RegisterType<NotDisposableClass>();
            _container = builder.Build();
        }

        // Case 1: When finished, only coso2 is cleaned from memory.
        private void Case1()
        {
            DisposableClass coso1 = _container.Resolve<DisposableClass>();
            coso1.DoSomething();
            
            NotDisposableClass coso2 = _container.Resolve<NotDisposableClass>();
            coso2.DoSomething();
        }

        // Case 2: When finished, only coso2 is cleaned from memory.
        private void Case2()
        {
            DisposableClass coso1 = _container.Resolve<DisposableClass>();
            coso1.DoSomething();
            coso1.Dispose();
            
            NotDisposableClass coso2 = _container.Resolve<NotDisposableClass>();
            coso2.DoSomething();
        }

        // Case 3: Both coso1 and coso2 are released from memory.
        private void Case3()
        {
            using (var scope = _container.BeginLifetimeScope())
            {
                DisposableClass coso1 = scope.Resolve<DisposableClass>();
                coso1.DoSomething();
                coso1.Dispose();
            
                NotDisposableClass coso2 = scope.Resolve<NotDisposableClass>();
                coso2.DoSomething();
            }
        }

        // Case4: Both coso1 and coso2 are released.
        private void Case4()
        {
            _scope1 = _container.BeginLifetimeScope();
            DisposableClass coso1 = _scope1.Resolve<DisposableClass>();
            coso1.DoSomething();
            coso1.Dispose();
            
            _scope2 = _container.BeginLifetimeScope();
            NotDisposableClass coso2 = _scope2.Resolve<NotDisposableClass>();
            coso2.DoSomething();

            _scope1.Dispose();
        }

  

Я не ожидал результатов от Case1 и Case2, и эти результаты вызывают огромную проблему с утечкой памяти в гораздо более сложном приложении, в котором широко используется autofac.
Никто в команде не ожидал, что ссылка на переменную, не ограниченную областью действия, может поддерживаться контейнером, когда класс реализует IDisposable (Case1), даже при ее удалении (Case2).

Регистрация как ExternallyOwned на самом деле не является решением, потому что модули, регистрирующие типы, на самом деле не знают, как, когда или где этот тип будет использоваться, за исключением, может быть, для этих одноэлементных фабрик. Это означает, что каждый модуль должен регистрировать ВСЕ типы как ExternallyOwned, что равносильно удалению половины функций autofac.

Использование областей жизненного цикла является хорошим решением для транзакционных операций, веб-API и операций с небольшим жизненным циклом, но есть много случаев, когда его использование не очень полезно. Допустим, у вас есть последовательность элементов, которые периодически повторяются, и каждый шаг требует создания нового экземпляра одноразового объекта. Для этого мы используем SingleInstanceFactories, которые фактически находятся в верхней области построения. Мы заметили, что каждый элемент, созданный этой фабрикой, который использует разрешение контейнера, на самом деле не удаляется из памяти, даже если вы закрываете окно, в котором была создана эта последовательность, даже при вызове Dispose .

Мы как бы обсуждаем возможные решения, и мы решили передать новую область для каждой фабрики на запрос на создание и сохранить эту область, а также запрошенный экземпляр, но это кажется довольно трудоемким. Потому что вам нужно управлять в словаре всеми используемыми областями и тем, к какому экземпляру он принадлежит.

Для меня, когда область действия переменной освобождается, что относится к случаям Case1 и Case2, переменная должна быть удалена как .NET framework будет работать сам по себе (так же, как coso2 выпускается во всех случаях). Или, по крайней мере, имейте в виду, что в случае 2 мы принудительно вызываем Dispose() для экземпляра объекта InsntancePerDependency и впоследствии освобождаем его из памяти.

Не имеет значения, сколько раз вы принудительно выполняете сборку, переменная не будет удалена из памяти, пока вы не удалите контейнер, с которым в интерфейсных приложениях нелегко обращаться.

Итак, подведем итог:

  • является ли случай 1 и случай 2 ожидаемым поведением удаления объекта в Autofac? или это ошибка?
  • Есть ли способ указать контейнеру освободить ресурсы, не удаляя его? или конкретный экземпляр, созданный им?

Комментарии:

1. Есть некоторая запутанная терминология. В вопросе упоминается класс, который не может быть удален. Я не знаю, что это значит. Кроме того, удаление класса не имеет ничего общего с «очисткой его из памяти». Сборщик мусора делает это, когда ссылка выходит за пределы области видимости. Это затрудняет понимание ожидаемого поведения.

2. Привет. Вы правы. Когда я сказал, что другой экземпляр, который не реализует IDisposable, удален, я пытался подразумевать, что он удален из памяти. В любом случае я отредактирую вопрос, чтобы этот момент был ясен. После некоторого дополнительного чтения управления памятью autofacs кажется, что это ожидаемое поведение. Не имеет значения, заканчивается ли область видимости переменной, поскольку autofac будет содержать ссылку на любой созданный им экземпляр IDisposable, пока контейнер не будет освобожден

Ответ №1:

Судя по результатам, которые я вижу, и отсутствующему коду о том, как вы выполняли свои тесты, я почти уверен, что проблема связана с вашей настройкой тестирования, а не с Autofac. Разговор об автоматическом удалении в их документации и четко говорится:

Затем вы можете зарегистрировать свой компонент по мере необходимости, и в конце каждой области жизненного цикла, в которой разрешен компонент, будет вызываться метод Dispose() для компонента.

Время жизни Form1 не контролируется AutoFac (я предполагаю), поэтому он не может знать, когда автоматически удалять объект. В случае 3 4 вы сообщаете ему, когда область жизни закончилась, чтобы таким образом он мог правильно утилизировать объект.

tl; dr Неработающая настройка тестирования и непонимание срока службы компонентов приводят к неожиданным результатам.

Комментарии:

1. Здравствуйте. Я не прикрепил весь код. Form1 не является частью вопроса, поскольку он даже не зарегистрирован в контейнере. Проблема связана с внутренними переменными coso1 и coso2. В любом случае, после прочтения этого: nblumhardt.com/2011/01/an-autofac-lifetime-primer похоже, это ожидаемое поведение от Autofac

Ответ №2:

Для завершения я отвечу на свой вопрос так, как я его уже знаю.

Было очень полезно прочитать следующую ссылку: https://nblumhardt.com/2011/01/an-autofac-lifetime-primer /

  • является ли случай 1 и случай 2 ожидаемым поведением удаления объекта в Autofac? или это ошибка?

Это автоматическое нормальное поведение. Aufofac будет содержать ссылку на любой созданный им экземпляр IDisposable, и по этой причине GC не освободит этот ресурс, пока не будет освобожден контейнер autofac в этой области. Важно знать об этом, потому что вы можете подумать, что Net framework собирается освободить эти переменные, поскольку их область действия в коде закончилась. С другой стороны, было бы неплохо, если бы Autofac мог заметить, является ли он единственным, содержащим ссылку на экземпляр, и освободить его.

  • Есть ли способ указать контейнеру освободить ресурсы, не удаляя его? или конкретный экземпляр, созданный им?

Невозможно принудительно освободить конкретный экземпляр, созданный контейнером. Единственный доступный доступный вариант — следовать рекомендациям, использовать BeginLifeTimeScope перед вызовом любого контейнера resolve и каким-то образом удерживать ссылку на область жизни, чтобы освободить ее при необходимости.

Хотя это тоже может быть хорошей функцией.

Спасибо за ответы и извините, если какая-то часть текста не была прямой.