依赖项注入中的Blazor(服务器)作用域对象创建多个实例

出于演示目的,假设我有一个名为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); 
        }
    }
}

它具有一种在启动后运行的方法,该方法每5秒将其ID写入控制台。

在启动类中,将其添加为范围服务:

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() while循环,而另一个实例继续运行。如果我运行 Initialize(),则会出现一个新实例,并且每次我运行 Initialize()时,都会生成新实例,并且它们都将使用其唯一ID写入控制台。我可以无限创建任意数量的内容。

我以为将Scoped 服务注入DI可以确保每个电路有该对象的单个实例?我还尝试在 OnAfterRender()覆盖范围内进行初始化,以防预渲染过程创建双重实例(尽管这不能解释为什么我可以在注入了服务的页面中创建如此多的实例)。

是否存在无法正确处理的问题?除了Mainlayout之外,还有没有更好的位置初始化StateManager?

yxsong1972 回答:依赖项注入中的Blazor(服务器)作用域对象创建多个实例

  

我还尝试在OnAfterRender()覆盖范围内进行初始化,以防预渲染过程创建双重实例

这是由于预渲染而导致的,StateManager未处理。

但是您不能通过将初始化放在OnAfterRender()中来避免它。一种简单的方法是改为使用RenderMode.Server

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

由于您的StateManager需要StateManagerEx的知识,所以我们首先以一个虚拟的StateManagerEx为例,这比您的情况还容易:

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

Layout模式下以RenderMode.Server渲染时,

<p> @StateManagerEx.Id </p>

您只会获得一次ID。但是,如果以RenderMode.ServerPrerendered模式进行渲染,则会发现:

  1. 当浏览器向服务器发送请求时(但在建立Blazor连接之前),服务器会预先渲染该应用并返回HTTP响应。这是第一次创建StateManagerEx
  2. 然后在建立Blazor连接之后,创建另一个StateManagerEx

我创建了一个屏幕录像,并将每帧的持续时间增加了+100ms,您可以看到它的行为与我们上面所描述的完全相同(Id被更改):

enter image description here

StateManager也是如此。在ServerPrerendered模式下进行渲染时,将有两个StateManager,一个在Blazor连接建立之前创建,另一个在电路中。因此,您将看到两个实例正在运行。

  

如果我运行Initialize(),则会出现一个新实例,并且每次运行Initialize()时,都会生成一个新实例,并且它们都将使用其唯一ID写入控制台。

无论何时运行Initialize(),都会创建一个新的Guid。但是,StateManager实例保持不变(StateManager.IdInitialize()更改时)。

  

是否存在无法正确处理的问题?

您的StateManager 未实现IDisposable 。如果我按以下方式更改课程:

public class StateManager : IDisposable
{
    ...
}

即使我在App模式下渲染ServerPrerendered,每个连接一次也只能有一个91238a28-9332-4860-b466-a30f8afa5173 checking in...

enter image description here

本文链接:https://www.f2er.com/3155671.html

大家都在问