Search code examples
c#autofacfactory

Autofac Keyed Factory: Same Concrete Implementation For Different Enum Values


I want to create a field validator factory using autofac's built in IIndex<TKey, TValue> that will take in an enum value and return me an IFieldValidator. My validation factory should either return a concrete implementation of FieldValidator or ConditionalFieldValidator based upon a supplied token type enum value.

Service
User
Candidate
Unknown

I want to return a ConditionalFieldValidator when the token type is Service and I want to return a FieldValidator when the token type is either User, Candidate or Unknown

It appears that ContainerBuilder's Keyed method only accepts one parameter which forces me to register the components like this:

builder.RegisterType<ConditionalFieldValidator>().Keyed<IFieldValidator>(TokenType.Service);
builder.RegisterType<FieldValidator>().Keyed<IFieldValidator>(TokenType.User);
builder.RegisterType<FieldValidator>().Keyed<IFieldValidator>(TokenType.Candidate);
builder.RegisterType<FieldValidator>().Keyed<IFieldValidator>(TokenType.Unknown);

Is there a better way to do this that doesn't duplicate the FieldValidator registration? Would be nice to be able to pass multiple token types to the Keyed method.

EDIT

I ended up creating an extension method that incorporates Alistair and Cyril's answers.

public static ContainerBuilder RegisterTypeKeyed<TConcrete, TAbstract>(this ContainerBuilder containerBuilder, params object[] values)
{
    var registration = containerBuilder.RegisterType<TConcrete>();
        
    foreach(var value in values)
        registration.Keyed<TAbstract>(value);

    return containerBuilder;
}

This allows you to register your keyed components in one line from your container factory.

builder.RegisterTypeKeyed<ConditionalFieldValidator, IFieldValidator>(AuthTokenType.Service);
builder.RegisterTypeKeyed<FieldValidator, IFieldValidator>(AuthTokenType.Candidate, AuthTokenType.Unknown, AuthTokenType.User);

Solution

  • You should just be able to stack multiple Keyed calls onto the registration (like you can with As<T>), like so:

    builder.RegisterType<FieldValidator>().Keyed<IFieldValidator>(TokenType.User)
                                          .Keyed<IFieldValidator>(TokenType.Candidate)
                                          .Keyed<IFieldValidator>(TokenType.Unknown);
    

    That keeps the same registration of the FieldValidator component (including any instance sharing behaviour), but registers multiple services.