0

My goal is to serve an interface implementation depending on the scope.

I figured to solve that with the following code, but this results in A being overwritten by B

var containerBuilder = new ContainerBuilder();

containerBuilder.RegisterType<ImplA>().As<IMyInterface>().InstancePerMatchingLifetimeScope(MyScope.A);
containerBuilder.RegisterType<ImplB>().As<IMyInterface>().InstancePerMatchingLifetimeScope(MyScope.B);

IContainer container = containerBuilder.Build();
using (ILifetimeScope lifetimeScope = container.BeginLifetimeScope(MyScope.A))
{
    IMyInterface c = lifetimeScope.Resolve<IMyInterface>();
    Console.WriteLine(c.GetType());
}

using (ILifetimeScope lifetimeScope = container.BeginLifetimeScope(MyScope.B))
{
    var c = lifetimeScope.Resolve<IMyInterface>();
    Console.WriteLine(c.GetType());
}

I know I can register Types within a lifetimescope in the following way, but I want to configure this from within a Module

using (ILifetimeScope lifetimeScope = container.BeginLifetimeScope(MyScope.A, builder =>
{
    containerBuilder.RegisterType<ImplA>().As<IMyInterface>();
}))

Is there a way to solve this at Module level?

Pepernoot
  • 3,409
  • 3
  • 21
  • 46

1 Answers1

0

My current solution is as follows

public static class AutofacExtension
{
    private class ScopeModule
    {
        private readonly Action<ContainerBuilder> _action;

        public ScopeModule(object lifetimeScopeTag, Action<ContainerBuilder> action)
        {
            _action = action;
        }

        public void ConfigurationAction(ContainerBuilder lifetimeScope)
        {
            _action(lifetimeScope);
        }
    }

    public static void ForScope(this ContainerBuilder containerBuilder, object scope, Action<ContainerBuilder> action)
    {
        containerBuilder
            .RegisterInstance(new ScopeModule(scope, action))
            .Keyed<ScopeModule>(scope)
            .SingleInstance();
    }

    public static ILifetimeScope BeginModuleLifetimeScope(this IContainer container, object scope, Action<ContainerBuilder> configurationAction = null)
    {
        return container.BeginLifetimeScope(scope, lifeTimeScopeContainerBuilder =>
        {
            var scopeModules = container.ResolveKeyed<IEnumerable<ScopeModule>>(scope);
            foreach (ScopeModule scopeModule in scopeModules)
            {
                scopeModule.ConfigurationAction(lifeTimeScopeContainerBuilder);
            }
            configurationAction?.Invoke(lifeTimeScopeContainerBuilder);
        });
    }
}

Usage

var containerBuilder = new ContainerBuilder();

containerBuilder.ForScope(MyScope.A, scope => scope.RegisterType<ImplA>().As<IMyInterface>());
containerBuilder.ForScope(MyScope.B, scope => scope.RegisterType<ImplB>().As<IMyInterface>());

IContainer container = containerBuilder.Build();
using (ILifetimeScope lifetimeScope = container.BeginModuleLifetimeScope(MyScope.A))
{
    IMyInterface c = lifetimeScope.Resolve<IMyInterface>();
    Console.WriteLine(c.GetType());
}

using (ILifetimeScope lifetimeScope = container.BeginModuleLifetimeScope(MyScope.B))
{
    var c = lifetimeScope.Resolve<IMyInterface>();
    Console.WriteLine(c.GetType());
}
Pepernoot
  • 3,409
  • 3
  • 21
  • 46
  • Any solution to this problem is going to be some variation on this theme, so what you have - if it works for you - is just fine. There is not some magic/native mechanism for supporting what you're asking for, so you didn't miss anything. – Travis Illig Sep 28 '20 at 13:59
  • I posted my current solution here https://stackoverflow.com/a/64103657/6720987 – Pepernoot Sep 28 '20 at 14:20