Reputation: 3367
For demonstration purposes let's say I have a class called 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);
}
}
}
It has a method that runs after it is initiated that writes it's Id to the console every 5 seconds.
In my Startup class I add it as a Scoped service:
services.AddScoped<StateManager>();
Maybe I am using the wrong location but in my MainLayout.razor file I am initializing it on OnInitializedAsync()
@inject Models.StateManager StateManager
...
@code{
protected override async Task OnInitializedAsync()
{
StateManager.Initialize();
}
}
When running the application after it renders the first page the console output is showing that there are 2 instances running:
bcf76a96-e343-4186-bda8-f7622f18fb27 checking in...
e5c9824b-8c93-45e7-a5c3-6498b19ed647 checking in...
If I run Dispose() on the object it ends the KeepSession() while loop on one of the instances but the other keeps running. If I run Initialize() a new instance appears and every time I run Initialize() new instances are generated and they are all writing to the console with their unique id's. I am able to create as many as I want without limit.
I thought injecting a Scoped<> service into the DI guaranteed a single instance of that object per circuit? I also tried initializing within the OnAfterRender() override in case the pre-rendering process was creating dual instances (although this does not explain why I can create so many within a page that has the service injected).
Is there something I am not handling properly? Is there a better location to initialize the StateManager aside from MainLayout?
Upvotes: 4
Views: 3364
Reputation: 25350
I also tried initializing within the OnAfterRender() override in case the pre-rendering process was creating dual instances
It is caused by pre-rendering & the StateManager
is not disposed.
But you cannot avoid it by putting the initialization within OnAfterRender()
. An easy way is to use the RenderMode.Server
instead.
<app>@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))@(await Html.RenderComponentAsync<App>(RenderMode.Server)) </app>
Since your StateManager
requires a knowledge on StateManagerEx
, let's firstly take a dummy StateManagerEx
as an example, which is easier than your scenario:
public class StateManagerEx
{
public StateManagerEx()
{
this.Id = Guid.NewGuid().ToString();
}
public string Id { get; private set; }
}
When you render it in Layout
in RenderMode.Server
Mode:
<p> @StateManagerEx.Id </p>
You'll get the Id only once. However, if you render it in RenderMode.ServerPrerendered
mode, you'll find that:
StateManagerEx
is created.Blazor
connection is established, another StateManagerEx
is created. I create a screen recording and increase the duration of each frame by +100ms
, you can see that its behavior is exactly the same as what we describe above (The Id gets changed):
The same goes for the StateManager
. When you render in ServerPrerendered
mode, there will be two StateManager
, one is created before the Blazor connection has been established, and the other one resides in the circuit. So you'll see two instances running.
If I run Initialize() a new instance appears and every time I run Initialize() new instances are generated and they are all writing to the console with their unique id's.
Whenever you run Initialize()
, a new Guid
is created. However, the StateManager
instance keeps the same ( while StateManager.Id
is changed by Initialize()
).
Is there something I am not handling properly?
Your StateManager
did not implements the IDisposable
. If I change the class as below:
public class StateManager : IDisposable
{
...
}
even if I render the App
in ServerPrerendered
mode, there's only one 91238a28-9332-4860-b466-a30f8afa5173 checking in...
per connection at the same time:
Upvotes: 12