Skip to content

sorensenmatias/ServiceProviderValidationExtensions

Repository files navigation

ServiceProviderValidationExtensions

This library enables more fine-grained control for registrations in ServiceProvider. These are especially useful for large complicated application.

What does this solve?

It can be critical for a dependency to only be registered once in ServiceProvider. By implementaion, ServiceProvider will resolve the first occourence in its list of services in case the service has been registered multiple times. However, this can be problematic in some sitations, as described below.

Situation 1 - avoid duplicate registrations

You may want to ensure a specfic ASP.NET middleware is only registered once for performance reasons. In large applications (with a lot of dependencies), it may not be obvious what codepaths that registeres middlewares. With this library you can easily specify this requirement and be sure that your application does not run this unneccesary instance of the same middleware.

Situation 2 - context specific registraions

You may have multiple implementaions of an interface, and you want to be ensure only the proper implementation is registered in a particular context.

Situation 3 - 'IEnumerable registrations'

If the same service type is registered multiple times in the ServiceProvider, all its implementations can be injected by taking a dependency on IEnumerable. But it can be important not to have the same implementation registered multiple times.

Notes on the existing TryAdd* methods

If the TryAddScoped, TryAddSingleton, etc methods are used consistently they will make sure a dependency is only registered once. But this will only partially resolve the situations described above.

Installation

There are two separate NuGet packages supplied:

  • ServiceProviderValidationExtensions which can be used with àny .NET app (think 'raw' ServiceProvider cases).
  • ServiceProviderValidationExtensions.Hosting which is designed to be used together with Microsoft.Extensions.Hosting (think ServiceProviders used within ASP.NET) .

Features

The following features are supplied:

Exclusive service registration

A service can be registered as exclusive, meaning that not more than once occourence of it can exists in the ServiceProvider. To register a service as exclusive, call SetExclusiveService on the IServiceCollection:

serviceCollection.SetExclusiveService<IMyService>()

Alternatively, overloads for the more common signatures of AddSingleton, AddScoped and AddTransient exists, e.g.:

serviceCollection.AddSingleton<MyService>(ServiceValidation.ExclusiveService);
serviceCollection.AddSingleton<IMyService, MyService>(ServiceValidation.ExclusiveService);

Exclusive implementation registration

An implementation can be registered as exclusive, meaning that not more than once occourence of the combination of the type of service and the implementation can exists in the ServiceProvider. To register a service as exclusive, call SetExclusiveImplementation on the IServiceCollection:

serviceCollection.SetExclusiveImplementation<IMyService, MyService>()

Alternatively, overloads for the more common signatures of AddSingleton, AddScoped and AddTransient exists, e.g.:

serviceCollection.AddSingleton<IMyService, MyService>(ServiceValidation.ExclusiveService);

Reporting

The reporting functionality makes it possible to get insights into the registrations in the ServiceProvider. It currently supports detecting duplicate service registrations.

void ConfigureReporting(ReportConfigurer rb)
{
    rb.OnDuplicateService(
        ds => Console.WriteLine($"{ds.ServiceType} is registered {dsc.ImplementationTypes.Count} times"),
        c => c.Except(typeof(IConfigureOptions<>)).Except<ILoggerProvider>());
}

Host.CreateDefaultBuilder()
    .UseServiceProviderExtendedValidation(ConfigureReporting)
    .Build();

How to wire it up

There are two ways of actually invoking the validation, as described below. In case any validation fails, a ServiceProviderValidationException is thrown.

Hosting extensions

Reference the ServiceProviderValidationExtensions.Hosting package and use the UseServiceProviderExtendedValidation extension method.

Host.CreateDefaultBuilder()
    .UseServiceProviderExtendedValidation()
    .Build();

or

Host.CreateApplicationBuilder()
    .ConfigureContainerWithServiceProviderExtendedValidation()
    .Build();

Manually building the ServiceProvider

Call BuildServiceProviderWithValidation on the IServiceCollection, e.g.

serviceCollection.BuildServiceProviderWithValidation()