Rob Bowman
Rob Bowman

Reputation: 8751

dotnet core dependency injection to extension method before BuildServiceProvider is called

I have a dotnet core 3.1 C# XUnit Project.

It has a Startup class containing the following Initialise method:

public static IServiceProvider Initialise()
    {
        var services = new ServiceCollection();
        ConfigureServices(services);
        return services.BuildServiceProvider();
    }

The ConfigureServices method contains the following:

private static void ConfigureServices(IServiceCollection services)
    {
        services.AddSharedConfiguration(_metadataTokens);
        services.AddLogging();
        services.AddTransient<IStructuredLogger>(x => new StructuredLogger(x.GetRequiredService<ILogger<PortfolioActivation>>()));

        services.AddClientApi(_metadataTokens);
    }

The AddClientApi is an extension method containing the following:

public static void AddClientApi(this IServiceCollection services, Dictionary<string, string> metadataTokens)
{
services.AddHttpClient(
    LocalSettings.HttpClientTypes.BrewinAvaloqHttpClient, client =>
    {
        client.BaseAddress = new Uri(metadataTokens[LocalSettings.ServiceConfigurationKeys.ClientApiBaseAddress]);
        client.DefaultRequestHeaders.Add(LocalSettings.HttpHeaderKeys.CacheControl, "no-cac
    }
).AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(5),
    TimeSpan.FromSeconds(10)
},
onRetry: (outcome, timespan, retryAttempt, context) =>
{
    //compile error here because I don't have structuredLogger      
    structuredLogger.LogInfo($"Failure on http request: {outcome}. This is retry attempt:[{retryAttempt}]"); 
}
));
}

My problem is in the onRety block, I'd like to make use of the "StructuredLogger" service that's been added to the IServiceCollection in the ConfigureServices method. I'm not sure how because at this point, the BuildServiceProvider method has not yet been called?

Upvotes: 3

Views: 1949

Answers (1)

eduherminio
eduherminio

Reputation: 1694

You should be able extract your service from IServiceProvider in the same fashion as you can do it here:

services.AddSingleton<MyClass>();
services.AddSingleton<MyClass2>(provider => provider.GetRequiredService<MyClass>());

As stated in this GitHub issue, .AddPolicyHandler() can be used to get access to IServiceProvider by doing:

services.AddHttpClient("...")
    .AddPolicyHandler((serviceProvider, request) => HttpPolicyExtensions.HandleTransientHttpError()
    .WaitAndRetryAsync(new[]
    {
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(5),
        TimeSpan.FromSeconds(10)
    },
    onRetry: (outcome, timespan, retryAttempt, context) =>
    {
        serviceProvider.GetRequiredService<IStructuredLogger>()
            .LogInfo($"Failure on http request: {outcome}. This is retry attempt:[{retryAttempt}]");
    }
));

Upvotes: 1

Related Questions