Search code examples
c#decoratorautofac

Autofac: How to decorate a single component that implements multiple services


Suppose I have a component that implements multiple interfaces:

public interface IService1 { }
public interface IService2 { }
public interface IService3 { }
public interface ICommonService : IService1, IService2, IService3 { }
public class CoreComponent : ICommonService { }

and its decorator

public class DecoratorComponent : ICommonService
{
    public DecoratorComponent(ICommonService coreComponent) { }
}

And then I need to resolve instances of IService1-3 and ICommonService as well. That's the problem.

Because I'd like to have a single instance of my CoreComponent and a single instance of the decorator. But after reading this issue I understood that this is impossible (as far as I understood).

At least, I want to have a single instance of my CoreComponent and multiple decorator instances, as many as the services, decorating that single core component instance. But when I registered them as following

builder
    .RegisterType<CoreComponent>()
    .As<ICommonService>()
    .As<IService1>()
    .As<IService2>()
    .As<IService3>()
    .SingleInstance();
builder.RegisterDecorator<DecoratorComponent, ICommonService>();
builder.RegisterDecorator<DecoratorComponent, IService1>();
builder.RegisterDecorator<DecoratorComponent, IService2>();
builder.RegisterDecorator<DecoratorComponent, IService3>();

and resolve IService1, I've got an instance of the decorator, decorating an instance of the decorator, decorating an instance of the core component. That's frustrating.

How to register that decorator properly to have only one level of decorators in my case, and have all the services resolved?

UPD: here is the sample code with my registration and the registration in the accepted answer: https://dotnetfiddle.net/PxjRqf
You can see that strange Autofac behavior when registering as above and how to fix that.


Solution

  • Instead of register the CoreComponent exposed as IService1, IService2 and IService3, you could register it only exposed as ICommonService and add registrations for the other interfaces forwarding them to an ICommonService resolution.

    var builder = new ContainerBuilder();
    
    // Register the type as singleton
    builder
      .RegisterType<CoreComponent>()
      .As<ICommonService>()
      .SingleInstance();
    
    // Register the decorator only once, for ICommonService.
    builder
      .RegisterDecorator<DecoratorComponent, ICommonService>();
    
    // Now, forward service resolution to ICommonService
    builder
      .Register(container => container.Resolve<ICommonService>())
      .As<IService1>()
      .As<IService2>()
      .As<IService3>();
    

    In the end, no matter what interface you resolve a CoreComponent from (either ICommonService or any IServiceX), Autofac will always end up resolving it from the ICommonService registration, which is decorated by the DecoratorComponent.

    Hope that helps.