0

I used this Best way to share data between two child components in Blazor to update a blazorcomponent from a service class to add notifications.

I added this in an API controller:

I added this to the POST of an API controller and also injected the notify service into the constructor.

And then display into the Blazor component like this: enter image description here But for some reason, this only works with services.AddSingleton, and not services.AddScoped.

But, services.AddScoped does work when you use the notify service between 2 blazor components like this. (This is a different component)

enter image description here

So, does a API controller use a different instance of the notify service? Or am I doing something wrong?

Thank you in advance. if you need more information, please let me know.

8
  • 2
    Why do you ask? Blazor Server and Web API do very different things. In a Blazor Server app, the shared data are specific to the current user's session. In Web API, there's no "current user", so you need to use session storage or some other, better mechanism to cache data between requests Commented Jul 26, 2021 at 14:26
  • I created the API controller inside the blazor project. So i thought they would both use the same service instance. Thank you for coming with a solution Commented Jul 26, 2021 at 14:32
  • What are you trying to do in the first place? Are you trying to share state between the two? Both are running on the server, so passing any data you want to the API controller is cheap. If you want to keep some user state, you can use the Blazor services. If you want to modify user data from an external service though, you'll have to manage this storage yourself, and probably use a database or other durable storage Commented Jul 26, 2021 at 14:37
  • I have an Azure queue serverless trigger. Inside that function I parse a file and I want to send the status to my blazor project. Once it gets in the API controller, i want to update the UI. Commented Jul 26, 2021 at 14:38
  • That's tricky, because there's no user when you receive an external call. Web API doesn't know which UI should be updated There may be multiple active tabs for the same user, or none. This problem remains even if you use a singleton, or database storage. You can use SignalR to find the correct user and send a notification with the change/notification data. Commented Jul 26, 2021 at 14:42

1 Answer 1

3

Do API controllers use a different instance of a service than Blazor components?

The answer is definitely yes for scoped services, no for singletons, but it gets complicated. But I suspect that's not your real question, and the real question is how to store user-specific data for both. Or modify a specific user's data from an external service

Why the services are different

A new controller instance is created to handle each HTTP request. The HTTP request defines a scope as far as DI is concerned, so a new service instance is created for each HTTP request and dispose after the request is processed.

This is A Good Thing, as it means expensive scoped services like a DbContext are disposed even if an error occurs. In the case of a DbContext, this give transaction-per-request semantics out of the box, without requiring any extra code.

Things are more complicated with Blazor Server. In this case, Blazor Server defines a scope per "user circuit" which is essentially a single tab. This means that scoped objects remain active as long the tab is active when the navigation happens on the client. The MVC part behaves the same way it did with ASP.NET Core MVC/Web API

This is similar to how a desktop application behaves and can cause nasty surprises when people assume that scoped services like a DbContest will be disposed "automagically". When you call SaveChangesAsync you may end up persisting changes you thought were discarded.

So even if you try to use a scoped service in Blazor Server, you may end up with a long-lived service in the Blazor components and a short-lived service in the Web API controllers.

In fact, since different scopes are used, the two services can't even find each other.

How DI and Scope work in Blazor Server

This is documented in ASP.NET Core Blazor dependency injection. The difference in a scope's lifetime is explained in Service Lifetime :

The Blazor Server hosting model supports the Scoped lifetime across HTTP requests but not across SignalR connection/circuit messages among components that are loaded on the client.

The Razor Pages or MVC portion of the app treats scoped services normally and recreates the services on each HTTP request when navigating among pages or views or from a page or view to a component.

Scoped services aren't reconstructed when navigating among components on the client, where the communication to the server takes place over the SignalR connection of the user's circuit, not via HTTP requests.

In the following component scenarios on the client, scoped services are reconstructed because a new circuit is created for the user:

  • The user closes the browser's window. The user opens a new window and navigates back to the app.
  • The user closes the last tab of the app in a browser window. The user opens a new tab and navigates back to the app.
  • The user selects the browser's reload/refresh button.

Notifying a user's tabs after an external call

From the comments :

I have an Azure queue serverless trigger. Inside that function I parse a file and I want to send the status to my blazor project. Once it gets in the API controller, i want to update the UI.

That's tricky. In this case the Web API is essentially acting as another user. The Blazor Server tabs need to be updated in response to what is essentially an external event.

Since it's Blazor Server though, assuming no load balancing is used, all the user circuits are running in the same process that handles API requests. One could raise an "event" in the API controller and have the Blazor components listen to it.

Luckily, that's exactly what libraries like Blazor.EventAggregator have implemented.

The Event Aggregator service is a singleton, registered with :

public void ConfigureServices(IServiceCollection services)
{
    services.AddEventAggregator();
}

Assuming the message only accepts a file name and a status:

public record FileEvent(string file,string status);

The Web API Controller will act as a publisher :


class MyController:ControllerBase
{

    private IEventAggregator _eventAggregator { get; set; }

    public MyController(private IEventAggregator eventAggregator)
    {
        _eventAggregator=eventAggregator;
    }

    ...

    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> Post(BlobInfo blobInfo)
    {
        await _eventAggregator.PublishAsync(new FileEvent(blogInfo.BlobName,"OK");
        return Ok();
    }
}

The Blazor Server components that care can inject IEventAggregator, listen for events, specify the events they care about and handle them.

In each component's code-behind, the service can be injected with :

[Inject]
private IEventAggregator _eventAggregator { get; set; }

The class also needs to implement the IHandler<T> interface for the events it cares about :

public class MyComponent : ComponentBase, IHandle<FileEvent>
{
    ...
    List<string> _messages=new List<string>;

    public async Task HandleAsync(FileEvent message)
    {
        _messages.Add($"{message.Name} worked");
        await InvokeAsync(StateHasChanged());
    }

...
}

The Razor code doesn't really need to change :

@foreach(var value in _messages)
{
   <p>@value</p>
}
Sign up to request clarification or add additional context in comments.

5 Comments

"The answer is definitely yes for scoped services." Do you mean that the object injected into the Blazor component is the same one injected into a Web Api ? Could you please prove your claim.
I can easily prove that you are wrong by a code sample, but you have first to prove that you're right by a code sample.
@enet read the docs. I said the exact opposite of what you understood. I said it's not, and can not be the same. I wrote an entire section titled Why the services are different how did you end up reading the object injected into the Blazor component is the same one injected into a Web Api ?
you've added the quotes later on. But if you say: "I said it's not", then why do you say: This is incorrect. For starters, with Blazor Server everything is running in the same process and use the same DI infrastructure. DI doesn't care about projects, it carers about scopes. " I see here a change of mind, except of mentioning scopes, which is really not relevant in the case of Web Api, as the scope is usually scoped to the duration of the request.
I still stand behind my answer: "API controllers do not use the same instance of your service that you inject into Blazor components." Please don't tell me read here and read there, just prove your claim by code... the new claim is that if a service is defined as Singleton, then it is shared by Blazor Server App and a Web Api App.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.