Search code examples
c#dependency-injectionautofac

Autofac register and resolve with Names


I'm trying to register objects by their names, and later on take them in another type's ctor as parameters, during registration.

Hope my example will be clear enough, here it is:

public class Obj : IObj
{
    public class Obj(string name)
}

I register the following objects like this :


public void RegisterMyObj(string name)
{
    // Keyed with the object name
    builder.Register<Obj>().Named<IObj>(name).WithParameter(name).SingleInstance();
}

public class ObjectsHolder : IObjectsHolder
{
    public ObjectsHolder (List<IObj> objsToHold))
}

// I want to register my ObjectsHolder in the following way:
for example, this is how I want to call it from my code: 
    RegisterObjectsHolder(string Obj1Name, string Obj2Name)

public void RegisterObjectsHolder(params string[] objectsNames)
{
    builder.Register<IObjectsHolder>().WithParameters(// Here comes the magic code which I can't figure out. 
                                                      // I want objects holder to be registered with the Obj instances whose names were passed to this method, 
                                                      // is there a way to do this?)
                                                      )
}   

I'm not strict about the ways the registrations will look.. If you know of a way to accomplish this using different methods, that will also do.

Thanks in advance!


Solution

  • Instead of registering your services as "named", you may want to use "keyed" services. There is no way to specify services by their names. But you can use IIndex<TKey, TService> to retrieve a dictionary-like object with keyed services of specified type. So you can register:

    builder.RegisterType<Service1>().Keyed<IService>("key1");
    builder.RegisterType<Service2>().Keyed<IService>("key2");
    builder.RegisterType<Service3>().Keyed<IService>("key3");
    builder.RegisterType<Service4>().Keyed<IService>("key4");
    builder.RegisterType<Service5>().Keyed<IService>("key5");
    

    Later, in your constructor, you can inject:

    public Test(IIndex<string, IService> serviceDictionary)
    {
        var service1 = serviceDictionary["key1"];
    }
    

    I used string objects as keys, but you can introduce e.g. enum and use it as a key.

    EDIT:

    If you want to narrow down available services for some classes, you can e.g. introduce different enum types as keys.

    enum ServicesA { A1, A2, A3 }
    enum ServicesB { B1, B2 }
    

    Then, registratrions:

    builder.RegisterType<Service1>().Keyed<IService>(ServicesA.A1);
    builder.RegisterType<Service2>().Keyed<IService>(ServicesA.A2);
    builder.RegisterType<Service3>().Keyed<IService>(ServicesA.A3);
    builder.RegisterType<Service4>().Keyed<IService>(ServicesB.B1);
    builder.RegisterType<Service5>().Keyed<IService>(ServicesB.B2);
    

    Now, if you inject IIndex<SerivcesA, IService>, only Service1, Service2 and Service3 would be available, for IIndex<SerivcesB, IService> it would be Service4 and Service5.

    You can chain Keyed registration so if you join both registrations from above to

    builder.RegisterType<Service1>().Keyed<IService>(ServicesA.A1).Keyed<IService>("key1");` 
    

    etc., you could use both IIndex<YourEnum, IService> with only some of IService implementations or IIndex<string, IService> with all of them.