I\'m trying to write an ASP.NET Core 2.2 integration test, where the test setup decorates a specific service that would normally be available to the API as a dependency. The
Contrary to popular belief, the decorator pattern is fairly easy to implement using the built-in container.
What we generally want is to overwrite the registration of the regular implementation by the decorated one, making us of the original one as a parameter to the decorator. As a result, asking for an IDependency should lead to a DecoratorImplementation wrapping the OriginalImplementation.
(If we merely want to register the decorator as a different TService than the original, things are even easier.)
public void ConfigureServices(IServiceCollection services)
{
// First add the regular implementation
services.AddSingleton();
// Wouldn't it be nice if we could do this...
services.AddDecorator(
(serviceProvider, decorated) => new DecoratorImplementation(decorated));
// ...or even this?
services.AddDecorator();
}
The above code works once we add the following extension methods:
public static class DecoratorRegistrationExtensions
{
///
/// Registers a decorator on top of the previous registration of that type.
///
/// Constructs a new instance based on the the instance to decorate and the .
/// If no lifetime is provided, the lifetime of the previous registration is used.
public static IServiceCollection AddDecorator(
this IServiceCollection services,
Func decoratorFactory,
ServiceLifetime? lifetime = null)
where TService : class
{
// By convention, the last registration wins
var previousRegistration = services.LastOrDefault(
descriptor => descriptor.ServiceType == typeof(TService));
if (previousRegistration is null)
throw new InvalidOperationException($"Tried to register a decorator for type {typeof(TService).Name} when no such type was registered.");
// Get a factory to produce the original implementation
var decoratedServiceFactory = previousRegistration.ImplementationFactory;
if (decoratedServiceFactory is null && previousRegistration.ImplementationInstance != null)
decoratedServiceFactory = _ => previousRegistration.ImplementationInstance;
if (decoratedServiceFactory is null && previousRegistration.ImplementationType != null)
decoratedServiceFactory = serviceProvider => ActivatorUtilities.CreateInstance(
serviceProvider, previousRegistration.ImplementationType, Array.Empty