Blazor время жизни Scoped-сервиса
Делаю spa на blazor и при закрытии View и вызове метода Dispose у сервиса он не очищается полностью, т.к при следующем переходе на эту же View не вызывается конструктор у этого сервиса. Сервис в реальном проекте объявлен как Scoped. Создал максимально простой пример.
Немного пояснений про этот пример. Все свойства завязаны на ReactiveUI. Есть главная страница - MainView и вторая ProfileView, которая в себе имеет TabControl с двумя вкладками: FirstTabView и SecondTabView. Все View объявлены как Transient-сервис, а один сервис ProfileService как Scoped.
Поведение MainView.
Есть свойство Number, которое в конструкторе(MainViewModel) бесконечно инкрементируется пока не вызовется метод Dispose. Данный метод сделал для примера времени жизни Transient.
Поведение ProfileView
После первого рендера ui(OnAfterRenderAsync) вызывается фейковая загрузка в сервисе ProfileService.LoadProfileAsync. После загрузки показывается время создания ProfileView, время создания сервиса(для проверки создается ли он каждый раз) и время его последнего обновления. На первой вкладке показывается для примера имя, а на второй соединения, которые постоянно обновляются из ProfileView.
Моя проблема
При переходе между вкладками(First,Second) в TabControl подписки на свойства удаляются и все прекрасно работает, но если я уйду с ProfileView на MainView, то мне нужно освободить уже сам сервис ProfileService, т.к он мне не нужен. Он "освобождается", но при следующем попадании на страницу ProfileView конструктор у этого сервиса не вызывается. А т.к не вызывается у него конструктор, то и все подписки не могут получить свои данные, ведь в методе Dispose у этого сервиса я вызывал OnCompleted у каждого поля, то есть завершил будущие поступления обновлений.
Вопрос
Почему не вызывается конструктор ProfileService при переходе на ProfileView, если я до этого вызвал метод Dispose?
Ответы (1 шт):
Мне ответил единственный человек тут. В общем, в wasm-приложении нет никакой разницы между scoped и singleton. Чтобы управлять временем жизни scoped сервисов надо наследовать компонент от OwningComponentBase(есть генерик реализация). Если унаследоваться от этого компонента, то у нас очищаются все сервисы, которые были получены во время работы. На первый взгляд все отлично, но не когда у меня есть главное окно, в котором есть n окон и в каждом есть еще k окон. Если каждый отнаследовать от OwningComponentBase, то получается n + k(возможное) экземпляров этого класса, что мне не подходит.
На данный момент нашел 2 решения:
- Не брать в голову, что где-то какой-то сервис висит в памяти и оставлять его как scoped/singleton(нет разницы)
- Можно главный компонент отнаследовать от OwningComponentBase, а все дочерние от простого ComponentBase/ReactiveComponentBase. В главной ViewModel делаем публичные свойства нужных нам сервисов. После инициализации главной формы в качестве параметров передаем эти сервисы в дочерние формы.
Главная VM
public class MainViewModel : ViewModelBase
{
public IProfileService ProfileService { get; private set; }
public MainViewModel(IProfileService profileService)
{
ProfileService = profileService;
}
}
Главный компонент MainView.razor.cs
<h3>Main content...</h3>
<FirstTabView ProfileService="ViewModel.ProfileService"/>
Дочерний компонент FirstTabView.razor.cs
public partial class FirstTabView
{
[Parameter]
public IProfileService ProfileService { get; set; }
protected override void OnParametersSet()
{
ViewModel = new FirstTabViewModel(ProfileService);
}
}
Может существуют более изящные решения, но на данный момент я их не знаю/не нашел.
