Lapish
Lapish

Reputation: 176

Blazor lifetime scoped-service

I make a spa on a blazor and when i close the View and call the Dispose method on ther service, it is not completely cleared, because the next time when i switch to the same view, constructor of this service is not called. Service declared as Scoped. I created the simple example.

Some explanations aboout this example. There is a main page - MainView and a second ProfileView, which has a TabControl with two tabs: FirstTabView and SecondTabView. All Views are declared as Transient services and ProfileService is declared as Scoped.

MainView behavior

There is a Number property, which in the constructor (MainViewModel) is infinitely incremented until the Dispose method is called. This method is made for an example of the lifetime of the Transient.

ProfileView behavior

After the first ui render (OnAfterRenderAsync), a fake load is called in the ProfileService.LoadProfileAsync service. After loading, it shows the time the ProfileView was created, the time the service was created (to check if it is created each time) and the time it was last updated. The first tab shows the name and the second shows connections that are constantly updated from ProfileView.

enter image description here

When switching between tabs (First, Second) in TabControl, property subscriptions are deleted and everything works fine, but if I leave ProfileView and navigate to MainView, then i need to release the ProfileService itself, because i don't need it. It is "freed", but the next time navigate to ProfileView page, the service's constructor is not called. And since the constructor is not called from it, then all subscriptions cannot receive their data, because in the Dispose method of this service I called OnCompleted for each field.

Question?

Why isn't the ProfileService constructor called when i navigate to ProfileView if i've called Dispose before?

Upvotes: 1

Views: 2198

Answers (1)

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30167

You can't call Dispose to delete an instance of a service.

A Scoped service lives for the lifetime of the SPA session. Calling Dispose manually just runs whatever code you have placed in Dispose. The Scoped instance of the object isn't removed until the Dependency Injection container is destroyed. If you want to limit the life of a Scoped service to the lifetime of a component/page inherit from OwningComponentBase. Search the Internet for examples of how to use it, there are several.

Update to clarify after further (much later) comments.

In Blazor Server:

  1. There is an application DI container which is created when the Web Application starts. This contains all the Singleton Services.
  2. Each Hub session - which corresponds to a SPA session - creates it's own Scoped Container. The renderer uses this DI Container to request all injected services. Any dependant Scoped services on the requested service are also created in this container as are any Transient services. All Singleton services are pulled from the Application container.
  3. Owning Component, or any other class that creates it's own DI container will request services from that container and any dependant services will be created in that container. All Singleton services are pulled from the Application container.

In Blazor WASM:

  1. There is only one container, created when the SPA starts. In effect Singleton and Scoped are the same.
  2. Any Owning component creates it's own container as in Server.

There is an article here - https://www.codeproject.com/Articles/5341118/Using-Blazors-OwningComponentBase - that describes how to handle getting services from the Hub Scoped container when using OwningComponent.

Upvotes: 3

Related Questions