Blazor (Server) объект области в инъекции зависимостей, создавая несколько экземпляров - PullRequest
0 голосов
/ 06 ноября 2019

В демонстрационных целях, скажем, у меня есть класс с именем StateManager:

public class StateManager
{
    public StateManager()
    {
        IsRunning = false;
    }

    public void Initialize()
    {
        Id = Guid.NewGuid().ToString();
        IsRunning = true;
        KeepSession();
    }

    public void Dispose()
    {
        Id = null;
        IsRunning = false;
    }

    public string Id { get; private set; }
    public bool IsRunning { get; private set; }

    private async void KeepSession()
    {
        while(IsRunning)
        {
            Console.WriteLine($"{Id} checking in...");
            await Task.Delay(5000); 
        }
    }
}

У него есть метод, который запускается после его инициации, который записывает свой Id в консоль каждые 5 секунд.

В моем классе запуска я добавляю его как службу Scoped:

services.AddScoped<StateManager>();

Возможно, я использую неправильное местоположение, но в моем файле MainLayout.razor я инициализирую его на OnInitializedAsync ()

@inject Models.StateManager StateManager
...
@code{
    protected override async Task OnInitializedAsync()
    {
        StateManager.Initialize();
    }
}

При запуске приложения после того, как оно отображает первую страницу, вывод консоли показывает, что запущено 2 экземпляра:

bcf76a96-e343-4186-bda8-f7622f18fb27 проверка в ...

e5c9824b-8c93-45e7-a5c3-6498b19ed647 проверка в ...

Если я запускаю Dispose () на объекте он завершает цикл KeepSession () в одном из экземпляров, но другой продолжает работать. Если я запускаю Initialize () , появляется новый экземпляр, и каждый раз, когда я запускаю Initialize () , создаются новые экземпляры, и все они записывают в консоль свои уникальные идентификаторы. Я могу создавать сколько угодно без ограничений.

Я думал, что внедрение службы Scoped <> в DI гарантирует один экземпляр этого объекта на схему? Я также попытался инициализировать с помощью переопределения OnAfterRender () в случае, если в процессе предварительной визуализации создавались двойные экземпляры (хотя это не объясняет, почему я могу создать так много на странице, в которую вставлена ​​служба).

Есть что-то, с чем я не справляюсь должным образом? Есть ли лучшее место для инициализации StateManager помимо MainLayout?

1 Ответ

1 голос
/ 06 ноября 2019

Я также попытался инициализировать в переопределении OnAfterRender (), если в процессе предварительной визуализации создавались двойные экземпляры

Это вызвано предварительным рендерингом, а StateManager - нет. расположены.

Но вы не можете избежать этого, поместив инициализацию в OnAfterRender(). Самый простой способ - использовать вместо него RenderMode.Server.

<app>
    <strike>@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))</strike>
    <b>@(await Html.RenderComponentAsync<App>(RenderMode.Server)) 
</app></b>

Поскольку для вашего StateManager требуется знание StateManagerEx, давайте сначала возьмем в качестве пустышки StateManagerExпример, который проще, чем ваш сценарий:

public class StateManagerEx
{
    public StateManagerEx()
    {
        this.Id = Guid.NewGuid().ToString();
    }
    public string Id { get; private set; }
}

Когда вы отображаете его в Layout в RenderMode.Server Mode:

<p> @StateManagerEx.Id </p>

Вы получите Id только один раз. Однако, если вы отобразите его в режиме RenderMode.ServerPrerendered, вы обнаружите, что:

  1. Когда браузер отправляет запрос на сервер (но до того, как соединение Blazor было установлено), сервер предварительно отображаетПриложение и возвращает ответ HTTP. Это первый раз, когда создается StateManagerEx.
  2. И затем после установления соединения Blazor создается еще одно StateManagerEx.

Я создаю запись экрана и увеличиваю длительность каждого кадра на +100ms, вы можете видеть, что его поведение точно такое же, как мы описали выше (Идентификатор изменяется):

enter image description here

То же самое касается StateManager. Когда вы выполняете рендеринг в режиме ServerPrerendered, будет два StateManager, один из которых будет создан до того, как будет установлено соединение Blazor, а другой будет находиться в цепи. Таким образом, вы увидите два запущенных экземпляра.

Если я запускаю Initialize (), появляется новый экземпляр, и каждый раз, когда я запускаю Initialize (), создаются новые экземпляры, и все они записывают в консоль своиуникальные идентификаторы.

Всякий раз, когда вы запускаете Initialize(), создается новый Guid. Однако экземпляр StateManager сохраняет то же самое (в то время как StateManager.Id изменяется на Initialize()).

Есть что-то, с чем я не справляюсь должным образом?

Ваш StateManager не реализовал IDisposable. Если я изменю класс следующим образом:

public class StateManager : IDisposable
{
    ...
}

, даже если я рендерим App в режиме ServerPrerendered, на одно соединение одновременно будет только один 91238a28-9332-4860-b466-a30f8afa5173 checking in...:

enter image description here

...