1

I'm dealing with one problem. Imagine this example situation. I have one module e.g. UserModule with injected IUserRepository. IUserRepository can have more implementations e.g. IUserRepositorySql and IUserRepositoryDefault.

public class UserModule : IUserModule
{
    private readonly IUserRepository userRepository;

    public UserModule(IUserRepository userRepository)
    {
        if (userRepository == null) throw new ArgumentNullException("userRepository");
        this.userRepository = userRepository;
    }
}

I would like to define which implementation of IUserRepository will be used in UserModule. I want to avoid Factory pattern that is considered like IoC antipattern in Marc Seeman book and I would like achieve this only with container configuration.

I'm using LightInject and it has something like named services but it can be used only on the top level. I need something like this :

var container = new ServiceContainer();

container.Register<IUserRepository, UserRepositorySql>("Sql");
container.Register<IUserRepository, UserRepositoryDefault>("Default");
container.Register<IUserModule, UserModule>();

var instance = container.GetInstance<IUserModule>("Sql");

This code should return instance of IUserModule with injected UserRepositorySql instance, but of course, it doesn't.

Do you have some soulution for this in LightInject please?

I found a feature annotation in Lightinject where I can inject some properties of an appropriate types, but I don't like this solution so much.

Do you have any experience in some other IoC containers? How do you/which feature does solve this issue?

y0j0
  • 3,369
  • 5
  • 31
  • 52
  • I've read Mark Seemann's book, but can't recall he calling Factory an anti-pattern. Can you refer to the pages where he does so? – Steven Sep 08 '14 at 17:57
  • Can you explain why you actually need to be able to do this? – Steven Sep 08 '14 at 17:59
  • 1
    I know that Mark Seemann considers the Service Locator to be an anti pattern (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/), so that's how I read this question. – Leon Bouquiet Sep 08 '14 at 17:59
  • @Steven in chapter about anti-patterns you can find Control Freak anti-pattern. As an examples, Mark Seeman gave factory patterns (you have to use new keyword) – y0j0 Sep 08 '14 at 19:03
  • @Steven this is only simplified example. I need it because I need dynamically use other modules for some types of service calls. – y0j0 Sep 08 '14 at 19:05
  • 2
    I think you misinterpreted that chapter. Mark doesn't call Factories an anti-pattern. On page 139 he even says: "Abstract Factory is [...] useful in relation to DI because it can encapsulate complex logic that creates other DEPENDENCIES. It offers a good alternative to the complete transfer of control that's involved in full INVERSION OF CONTROL". – Steven Sep 08 '14 at 19:08
  • @Steven Yes you're right. Mark Seeman wrote that control freak is occurred very often in factory pattern but it is possible to implement factory in clean way – y0j0 Sep 08 '14 at 20:10

2 Answers2

2

I would have to agree both with Steven and Astrotrain here, but there is a third option available.

The following example uses parameters to resolve the correct implementation of the IUserRepository interface.

using LightInject;

class Program
{
    static void Main(string[] args)
    {
        var container = new ServiceContainer();
        container.Register<IUserRepository, UserRepositorySql>("Sql");
        container.Register<IUserRepository, UserRepositoryDefault>("Default");
        container.Register<string, IUserModule>(
            (factory, serviceName) => new UserModule(factory.GetInstance<IUserRepository>(serviceName)));

        var userModuleWithSqlRepository = container.GetInstance<string, IUserModule>("Sql");
        var userModuleWithDefaultRepository = container.GetInstance<string, IUserModule>("Default");
    }
}

public interface IUserModule { }

public class UserModule : IUserModule
{
    public UserModule(IUserRepository repository)
    {
    }
}

public interface IUserRepository { }

public class UserRepositorySql : IUserRepository { }

public class UserRepositoryDefault : IUserRepository { }
seesharper
  • 3,249
  • 1
  • 19
  • 23
1

I know that Unity supports specifying which named instance to use when performing constructor injection by means of the ResolvedParameter class (which works just like your imaginary construction):

container.Register<IUserModule, UserModule>(
    new InjectionConstructor(
        new ResolvedParameter<IUserModule>("Sql")));

However, most DI frameworks also support factory methods, and the above example could also be written in Unity as:

container.Register<IUserModule>(
    new InjectionFactory(cont => cont.Resolve<IUserModule>("Sql"));

In this case, you tell Unity that you want it to use your expression whenever it should create an IUserModule instance, and in that expression, you're free to use the container to resolve a specific instance.

I suspect that the latter construction could very well be possible with your DI framework as well.

Leon Bouquiet
  • 4,159
  • 3
  • 25
  • 36