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





Уничтожит ли ASP.NET мой экземпляр
ScopedAс ограниченной областью внутри вызова функции, чтобы что-то пошло не так?
Синглтон сможет использовать ScopedA, если этот класс или одна из его зависимостей не реализует IDisposable (или IAsyncDisposable).
Контейнер DI перестанет ссылаться ScopedA, когда область действия закончится. Но поскольку Singleton1 все еще содержит ссылку на него, ScopedA не будет собираться мусором.
Но в случае удаления ScopedA или одной из его зависимостей, они будут удалены (или могут быть удалены) до того, как Singleton1 завершит свою фоновую операцию. Это может привести к выдаче ObjectDisposedException при попытке Singleton1 получить доступ к ScopedA.
Хотя все это может работать, когда граф объектов для ScopedA не содержит одноразовых объектов, здесь все же есть много проблем, таких как:
ScopedA может и не реализовать IDisposable, обычно сложно отследить, справедливо ли это для всех его зависимостей. Таким образом, хотя Singleton1 может успешно вызвать ScopedA сегодня, добавление IDisposable позже в другой класс в системе может привести к поломке приложения.ScopedA зарегистрировано в области видимости объявления, указывает на то, что оно не является потокобезопасным. Передача его Singleton1, который обращается к нему в другом потоке (параллельно с основной операцией), может быть не очень хорошей идеей.Вместо того, чтобы передавать зависимость с ограниченной областью синглтону, который сохраняет зависимость после завершения ее области действия, позвольте синглтону разрешить свою собственную зависимость с областью действия из своей собственной области, которая существует на протяжении всей фоновой операции.
Хорошие моменты. Особенно акцент на том, что «это может работать сейчас, но может сломаться в любой момент в постоянно меняющемся продукте в этой необычной неожиданной установке».
Отлично, спасибо. Я не реализовал это так, потому что у меня уже было плохое предчувствие. Теперь у меня есть веская «причина» для этого чувства ;)
Это означает, что «Dispose()» всегда вызывается платформой, когда область действия «завершена»?
Ну, "всегда" - это сильно сказано. Бывают случаи, когда этого не происходит, например, при регистрации экземпляра с использованием метода расширения AddSingleton<T>(T instance). Но в целом да.
Но также помните, что когда переходные процессы разрешаются из корневой области, они удаляются только тогда, когда удаляется корневая область (т. е. сам контейнер). Другими словами, на одноразовые переходные процессы будут продолжать ссылаться, поэтому вам следует предотвратить любое разрешение из самой корневой области, поскольку это может привести к утечкам памяти.
Вы ждете метода. Если _serviceS хранит ссылку _serviceA для использования даже после вызова метода, да, у вас, вероятно, есть более серьезная проблема. Является ли ServiceA вообще одноразовым?