Julien Martin
Julien Martin

Reputation: 421

How to resolve repository without BuilderServiceProvider in ConfigureService?

I need to load list from repository in ConfigureService method, not in Configure method. I loop list to generate multiple authentication scheme.

This is my method to load my list, it's works but I've Warning about builder.Services.BuildServiceProvider();.

Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.

var listCarrierSetting = GetCarrierSetting();

List<Setting> GetCarrierSetting()
{
    List<Setting> result = new();

    try
    {
        var sp = builder.Services.BuildServiceProvider();
        
        var settingRepository = sp.GetRequiredService<ISettingRepository>();

        var task = Task.Run(async () => await settingRepository.GetAllWithDetails());
        
        result = task.Result as List<Setting>;
    }
    catch (Exception)
    {
        //some code
    }

    return result;
}

Is it possible to resolve and call my repository without use BuildServiceProvider() ?

Upvotes: 1

Views: 1162

Answers (2)

Julien Martin
Julien Martin

Reputation: 421

Thanks Markus, I create my own configuration provider as described here.

Configuration provider where I serialize settings data (AuthenticationScheme, ValidIssuer and some other properties....)

    public class CarrierConfigurationProvider : ConfigurationProvider
    {
        private readonly string _connectionString;

        public CarrierConfigurationProvider(string connectionString) =>
            _connectionString = connectionString;

        public override void Load()
        {
            var options = new DbContextOptionsBuilder<CarrierContext>()
                .UseSqlServer(_connectionString).Options;

            using var dbContext = new CarrierContext(options);

            var lstSettings = dbContext.Settings.ToList();
            Data = lstSettings.ToDictionary(c => $"SettingsCarrier:{c.Id}" , c => JsonConvert.SerializeObject(c));
        }
    }

Configuration Source :

    public class CarrierConfigurationSource : IConfigurationSource
    {
        private readonly string _connectionString;

        public CarrierConfigurationSource(string connectionString) =>
            _connectionString = connectionString;

        public Microsoft.Extensions.Configuration.IConfigurationProvider Build(IConfigurationBuilder builder) =>
            new CarrierConfigurationProvider(_connectionString);
    }

Configuration builder extension :

    public static class ConfigurationBuilderExtensions
    {
        public static IConfigurationBuilder AddCarrierConfiguration(
            this IConfigurationBuilder builder)
        {
            var tempConfig = builder.Build();
            var connectionString =
                tempConfig.GetConnectionString("CarrierSettingConnection");

            return builder.Add(new CarrierConfigurationSource(connectionString));
        }
    }

Next I use my custom configuration in Program.cs :

builder.Configuration.AddCarrierConfiguration();

var settings = builder.Configuration.GetCarrierSetting();

And GetCarrierSetting method who return a list of Setting:

        public static List<Setting> GetCarrierSetting(this IConfiguration configuration)
        {
            var result = configuration.AsEnumerable().Where(w => w.Key.StartsWith("SettingsCarrier:")).Select(s => s.Value);

            List<Setting> lstSetting = new();

            foreach(var item in result)
            {
                lstSetting.Add(JsonConvert.DeserializeObject<Setting>(item));
            }

            return lstSetting;
        }

So like this, I think it's good practice to load data in ConfigureServices method without call BuildServiceProvider.

Upvotes: 0

Markus
Markus

Reputation: 22456

ConfigureServices is the place to register the services in the service provider and - as the error message says - it should not build the service provider prematurely.

However, you can rely on the configuration in ConfigureServices. So an option is to move the settings for the authentication schemes to application configuration. You can use a built-in configuration provider (e.g. for appsettings.json, environment variables, ...) and store the settings in the respective location.

If these do not work for your requirements, you can also create a custom configuration provider as described here. The sample also shows how to use a temporary config that contains a connection string for connecting to the database.

Upvotes: 1

Related Questions