Adi Prasetyo
Adi Prasetyo

Reputation: 1042

How to create singleton service which depend on another service, with passing IServiceProvider?

Due to some restrictions on development when using .NET 8, I need to manage some reusable service. Those need to be either transient or singleton, but 1 of 3 service is depend on the rest.

public static IServiceCollection SetupLoggerFactory(this IServiceCollection services)
{
    services.AddSingleton<ILoggerFactory>(new LoggerFactory());
    return services;
}

public static IServiceCollection SetupKafkaProducerForFoo(this IServiceCollection services, IConfiguration config)
{
    services.AddSingleton<IProducerService>(new ProducerService());
    return services;
}

The last service

public static IServiceCollection GetAppLogger(this IServiceCollection services, IServiceProvider serviceProvider)
{
    services.AddSingleton<IAppLogger<Badboy.Models.LoggingEvent>>
            (new LoggerAdapter<Badboy.Models.LoggingEvent>(
                serviceProvider.GetService<ILoggerFactory>(), serviceProvider.GetService<IProducerService>()));
    return services;
}

I need to pass the Service provider argument, but I don't know how.

The startup.cs file:

var builder = WebApplication.CreateBuilder(args);

builder.Services.SetupLoggerFactory();
builder.Services.SetupKafkaProducerForFoo(builder.Configuration);
builder.Services.GetAppLogger(missingArg);

Somehow I cannot get the getRequiredService method, but when I do

var serviceProvider = app.Services;

builder.Services.GetAppLogger(serviceProvider);

it throws an error:

Unhandled exception. System.InvalidOperationException: The service collection cannot be modified because it is read-only.

at Microsoft.Extensions.DependencyInjection.ServiceCollection.ThrowReadOnlyException()
at Microsoft.Extensions.DependencyInjection.ServiceCollection.System.Collections.Generic.ICollection<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>.Add(ServiceDescriptor item)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(IServiceCollection services, Type serviceType, Object implementationInstance)

Upvotes: 0

Views: 52

Answers (1)

Guru Stron
Guru Stron

Reputation: 143098

You can use AddSingleton accepting lambda:

services.AddSingleton<IAppLogger<Badboy.Models.LoggingEvent>>
(sp => new LoggerAdapter<Badboy.Models.LoggingEvent>(
    sp.GetService<ILoggerFactory>(), sp.GetService<IProducerService>()))

So you will not need to pass the service provider to the GetAppLogger (arguably it should be AddAppLogger) and you can call it before invoking the builder.Build which makes the service collection immutable (hence the exception).

Note that based on the provided code just doing:

services.AddSingleton<IAppLogger<Badboy.Models.LoggingEvent>,
    LoggerAdapter<Badboy.Models.LoggingEvent>>();

Should also do the trick.

Or using open generic registration:

services.AddSingleton(typeof(IAppLogger<>), typeof(LoggerAdapter<>));

Upvotes: 1

Related Questions