Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can i run Scoped service inside a Transient or Singleton service

Here i have a Backgroung listner for Azure Service bus for Topic, and the listener is registered as Transient and serviceclient registered as Singleton like this,

public static IServiceCollection AddASBMessageListners(this IServiceCollection services, Assembly assembly)
{

    foreach (var listener in assembly.GetTypes().Where(a => !a.IsAbstract && a.IsDefined(typeof(QueueNameAttribute))))
    {
        Console.WriteLine("Registering listner " +listener.Name);

        services.AddTransient(typeof(IHostedService), listener);

    }
    return services;
}
public static IServiceCollection AddASBClient(this IServiceCollection services, IASBConfiguration configuration)
{
    var clientOptions = new ServiceBusClientOptions()
    {
        TransportType = configuration.TransportType
    };
    services.AddSingleton(s => new ServiceBusClient(configuration.ConnectionString,
        clientOptions));

    return services;
}

And the background listener is implements IHostedService and IDisposable, and this listener class is registered with Transient scope.

And the listener class has this method,

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

public override async Task ProcessMessage(BrowserDataDeleteEvent message)
{
    try
    {
        using var scope = _logger.BeginScope("Consuming UserAccountDeleteEvenet for {Id}", message.UserId);

        var command = new DeleteUserPageVisitDataCommand(message.UserId.ToString());
        await _mediator.Send(command);

        _logger.LogInformation("UserAccountDeleteEvenet completed for UserId {UserId}", message.UserId);
        
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error processing userId {UserId}", message.UserId);
        throw;
    }
}

in this project I’m using CQRS pattern with mediator, and handler method has injected repository and repository is registered as Scoped service, that’s where the exception comes in, when listener try to execute the send method, it says

MediatR.Unit from root provider because it requires scoped service <Repository>

I already tried IServiceProvider option, and it works inside handler class, but not in the Listener class ProcessMessage method, Is there any other way to use the same method inside within the ProcessMessage ?

when i use option inside listener class like this,

public override async Task ProcessMessage(BrowserDataDeleteEvent message)
{
    try
    {
        using(var scoped = _serviceProvider.CreateScope())
        {

            var scopedProcessingService = scoped.ServiceProvider.GetRequiredService<IMediator>();

            using var scope = _logger.BeginScope("Consuming UserAccountDeleteEvenet for {Id}", message.UserId);

            var command = new DeleteUserPageVisitDataCommand(message.UserId.ToString());
            await scopedProcessingService.Send(command);

            _logger.LogInformation("UserAccountDeleteEvenet completed for UserId {UserId}", message.UserId);
        }
        
        
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error processing userId {UserId}", message.UserId);
        throw;
    }
}

i get the following error, when app builds

System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Transient ImplementationType: Api.Listeners.UserAccountDeletedEventListener': Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.ServiceProvider' while attempting to activate 'Api.Listeners.UserAccountDeletedEventListener'.)'

thanks in advance.

>Solution :

The problem is that services are usually scoped because they either require time-sensitive resources (like a transaction or a database connection), or need cleanup once they have completed their task.

Singleton services never end (until the application quits), and transient services have no defined lifetime. So, the very things that scoped services require are not possible.

To circumvent that, you need to define a lifetime for your invocations against the scoped services and ensure that you end the lifetime correctly (like committing transactions if everything went well).

That’s done like this:

using (var scope = _serviceProvider.CreateScope())
{
    var service = scope.ServiceProvider.GetRequiredService<YourService>();
    service.DoSomething();

    scope.GetRequiredService<MyDbContext>().SaveChanges();
}

You might want to move the lifetime management and service resolution to a specific class to hide the service location.

Microsoft also has an approach documented about hosted services here: https://learn.microsoft.com/en-us/dotnet/core/extensions/scoped-service

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading