Search code examples
c#dependency-injectionautofac

Is it possible to register one type for multiple keys with Autofac assembly scanning?


I am trying to register on type (i.e. MyClass) for multiple Keys (i.e. 3, 4, 5) in AutoFac. So that, componentContext.ResolveKeyed<T>(3),componentContext.ResolveKeyed<T>(4) and componentContext.ResolveKeyed<T>(5) all return MyClass instance. I'm not sure how to do it as Keyed<IMyClass>() requires the type and not the values.

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
            .Where(type => type.IsAssignableTo<IMyClass>())
            .Keyed<IMyClass>(type => type.GetCustomAttribute<MyCustomAttribute>().VALUES)
            .AsImplementedInterfaces()
            .InstancePerLifetimeScope();

[MyCustomAttribute(3, 4, 5)]
class MyClass : IMyClass { }

class MyCustomAttribute : Attribute {
    public int[] VALUES { get; set; }

    public MyCustomAttribute(params int[] values) {
       this.Values = values;
    }
}

Solution

  • This is not supported in Autofac core, but you could easily make an extension method to do it for you.

    public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
        Keyed<TService>(
            this IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle> registration,
            Func<Type, object[]> serviceKeyMapping)
    {
        var serviceType = typeof(TService);
        return registration
            .AssignableTo(serviceType)
            .As(t => serviceKeyMapping(t).Select(key => new KeyedService(key, serviceType)));
    }
    

    It should be roughly a drop-in replacement for what you're trying to do.

    Autofac is pretty easy to extend this way by using the existing extension methods as a starting place for a lot of working examples. The one I put here is a very slight modification on the existing Keyed<T> extension for assembly scanning. Since not every use case can possibly be covered, this is the "official solution" for filling gaps like this.