Передача экземпляра ограниченной службы в синглтон

У меня есть служба с ограниченной областью ScopedA, которую я внедряю через DI в другую службу с ограниченной областью ScopedB.
Я также добавляю третий сервис Singleton Singleton1:

public class ScopedB
{
    ScopedA _scopedA;
    Singleton1 _singleton1;

    public ServiceB(ScopedA scopedA, Singleton1 singleton1)
    {
       _scopedA = scopedA;
       _singleton1 = singletonA;
    }
    
    public async Task DoSomethingAsync()
    {
       await _singleton1.PerformALongerOperationWichNeedsServiceAAsync(_scopedA);
    }   
}

Singleton1 существует только один раз за всю жизнь процесса, тогда как ScopedA и ScopedB существуют только во время HTTP-запроса (с ограниченной областью действия).

Что произойдет, если я передам экземпляр _scopedA функции _singleton1, которая обрабатывает что-то в фоновом режиме в течение более длительного времени?

Допустим, мой запрос уже выполнен и DoSomethingAsync уже выполнен, но синглтон-сервис все еще что-то делает с _scopedA.

Уничтожит ли ASP.NET мой экземпляр ScopedA с ограниченной областью внутри вызова функции, чтобы что-то пошло не так? Или можно что-то подобное сделать без проблем?

Я знаю, что не рекомендуется вводить _scopedA непосредственно в _singleton1, поскольку это нарушит область применения.

Мой пример также нарушает рамки?

Если это актуально, я использую ASP.NET 6 (в качестве API) с помощью встроенного контейнера DI (Microsoft.Extensions.DependencyInjection).

Вы ждете метода. Если _serviceS хранит ссылку _serviceA для использования даже после вызова метода, да, у вас, вероятно, есть более серьезная проблема. Является ли ServiceA вообще одноразовым?

Ralf 08.05.2024 14:49

@DavidMason Рекомендуется использовать IServiceProvider в вашем одноэлементном классе, внедрив его в синглтон, чтобы создать сервис с ограниченной областью действия. как использовать внедрение-зависимостей-в-одиночке или, как рекомендует MSDN, используйте IServiceScopeFactory - Использовать службы с ограниченной областью внутри BackgroundService

Ryan Wilson 08.05.2024 15:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Уничтожит ли ASP.NET мой экземпляр ScopedA с ограниченной областью внутри вызова функции, чтобы что-то пошло не так?

Синглтон сможет использовать ScopedA, если этот класс или одна из его зависимостей не реализует IDisposable (или IAsyncDisposable).

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

Но в случае удаления ScopedA или одной из его зависимостей, они будут удалены (или могут быть удалены) до того, как Singleton1 завершит свою фоновую операцию. Это может привести к выдаче ObjectDisposedException при попытке Singleton1 получить доступ к ScopedA.

Хотя все это может работать, когда граф объектов для ScopedA не содержит одноразовых объектов, здесь все же есть много проблем, таких как:

  1. Хотя ScopedA может и не реализовать IDisposable, обычно сложно отследить, справедливо ли это для всех его зависимостей. Таким образом, хотя Singleton1 может успешно вызвать ScopedA сегодня, добавление IDisposable позже в другой класс в системе может привести к поломке приложения.
  2. Тот факт, что ScopedA зарегистрировано в области видимости объявления, указывает на то, что оно не является потокобезопасным. Передача его Singleton1, который обращается к нему в другом потоке (параллельно с основной операцией), может быть не очень хорошей идеей.
  3. Такая конструкция может сбить с толку других разработчиков, поскольку общеизвестно, что экземпляры Scoped не следует использовать параллельно в нескольких потоках. Когда я наткнусь на такой код, я, скорее всего, остановлю работу и начну исследовать этот код. Я начну разговаривать с другими разработчиками. Это означает, что, даже если вы поняли, что это действительно правильное рабочее решение, другие разработчики легко потратят (потратят) время на этот код.

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

Хорошие моменты. Особенно акцент на том, что «это может работать сейчас, но может сломаться в любой момент в постоянно меняющемся продукте в этой необычной неожиданной установке».

Ralf 08.05.2024 16:09

Отлично, спасибо. Я не реализовал это так, потому что у меня уже было плохое предчувствие. Теперь у меня есть веская «причина» для этого чувства ;)

David Mason 08.05.2024 16:32

Это означает, что «Dispose()» всегда вызывается платформой, когда область действия «завершена»?

David Mason 08.05.2024 16:49

Ну, "всегда" - это сильно сказано. Бывают случаи, когда этого не происходит, например, при регистрации экземпляра с использованием метода расширения AddSingleton<T>(T instance). Но в целом да.

Steven 08.05.2024 20:58

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

Steven 08.05.2024 20:59

Другие вопросы по теме