IHttpClientBuilder with DelegatingHandler creates additional scope in Blazor webassembly

73 Views Asked by At

I have the following situation:

builder.Services.AddHttpClient...
    .AddHttpMessageHandler<CustomMessageHandler>();

builder.Services.AddScoped<ScopedService>();

This scoped service, is injected in the message handler, and in a page code. My problem is that the objects are not the same. There are two separate scopes, and two separate instances of ScopedService.

The first and most obvious solution was to inject the service as Singleton. While working solution, this is not ok, because many services or libs have "AddSomething" that behind it has "AddScoped".

The second solution I found was recommending SuppressHandlerScope: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.http.httpclientfactoryoptions.suppresshandlerscope?view=dotnet-plat-ext-8.0

Did not work either, because it could not access scoped services from root.

The third solution I found was recommending IHttpContextAccessor, However this here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-6.0#ihttpcontextaccessorhttpcontext-in-razor-components-blazor, concerns me a bit.

I would like to:

Prevent the creation of second scope in the webassembly app.

Or

Access the same scope from the DelegatingHandler, that the pages are using.

(Or any other suggestions how to avoid creating more than one instance of each injected service)

Edit: Why is it a problem? To be specific, I am using a component lib. That lib injects Scoped services . One of those scoped services has a list of items. The components on the page use this list from the service.

Obviously modifying a copy of this list will do nothing good for the components.

The service gets two instances because it is scoped and the HttpClientFactory is making another scope.

Suppressing the scope creation option, makes the builder skip "CreateScope()", but the builder itself is singleton, so you cannot use scopes anymore.

I want to force the HttpClient to use the same scope, as the webassembly, and it doesn't seem like there is any solution.

1

There are 1 best solutions below

0
Shahar Shokrani On

The best solution is re-thinking about your design, but if its not possible, you can try something like a ScopeHolder injected with .net's IServiceScopeFactory:

public class CustomScopeHolder
{
    private readonly IServiceScopeFactory _scopeFactory;
    private IServiceScope _scope;

    public CustomScopeHolder(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public void CreateScope()
    {
        _scope = _scopeFactory.CreateScope();
    }

    public T GetService<T>()
    {    
        return _scope.ServiceProvider.GetRequiredService<T>();
    }

Register it as a Singleton of course:

services.AddSingleton<CustomScopeHolder>();

The the you can use it it the lifecycle of code page + custom message (maybe via middleware?):

var scopeHolder = context.RequestServices.GetRequiredService<CustomScopeHolder>();
scopeHolder.CreateScope();

Then you can easily inject it to (via constructor or whatever):

1. code page
2. custom massage

And use it like:

var scopedService = _scopeHolder.GetService<ScopedService>();