7

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!

2 Answers2

15

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.

codeConcussion
  • 12,739
  • 8
  • 49
  • 62
tdragon
  • 3,209
  • 1
  • 16
  • 17
  • That is a good solution although what is bothering me is that I don't want all the possible services to be available for some classes. A mistake of Key will get me the wrong service. I would want an IIndex<> with not all the keys avaiable, if that's possible somehow. –  Mar 14 '16 at 23:39
0

I like to register by naming convention.

 var assembly = Assembly.GetExecutingAssembly();
 builder.RegisterAssemblyTypes(assembly).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();

Then just place the dependencies in the constructor.

 private IUserService UserService { get; set; }
    public UserController(IUserService userService)
    {
        UserService = userService;
    }

This way I only need to stick to my naming conventions to get the job done. Honestly after initial setup of a project, I rarely need to look at autofac again.

MrSmith
  • 84
  • 7
  • 1
    Thats not what I meant, Say I have 100 services implemnting the same interface IService, each registered with it's name as the key. I want to register a Layer that takes only 3 of those services by specifying and giving their names to the registration of the layer. The Layer's ctor: Public Layer (IEnumerable services). Those service will be the ones with the names I specified at the registration of the Layer. –  Mar 13 '16 at 22:04
  • @S.Peter - It sounds like you need to use the [strategy pattern](http://stackoverflow.com/questions/31950362/factory-method-with-di-and-ioc/31971691#31971691). Don't use the DI container to implement business rules, that is what your application code (and patterns) are for. – NightOwl888 Mar 14 '16 at 22:52