依赖项注入-命名的依赖项

本文关键字:依赖 注入 | 更新日期: 2023-09-27 18:21:03

我正在处理一个问题。想象一下这个例子。我有一个模块,例如带有注入IUserRepository的UserModule。IUserRepository可以有更多的实现,例如IUserRepositorySql和IUserRepositoryDefault。

public class UserModule : IUserModule
{
    private readonly IUserRepository userRepository;
    public UserModule(IUserRepository userRepository)
    {
        if (userRepository == null) throw new ArgumentNullException("userRepository");
        this.userRepository = userRepository;
    }
}

我想定义哪个IUserRepository实现将在UserModule中使用。我想避免像Marc Seeman书中的IoC反模式那样的工厂模式,我只想通过容器配置来实现这一点。

我使用的是LightInject,它有一些类似于命名服务的东西,但它只能在顶级上使用。我需要这样的东西:

var container = new ServiceContainer();
container.Register<IUserRepository, UserRepositorySql>("Sql");
container.Register<IUserRepository, UserRepositoryDefault>("Default");
container.Register<IUserModule, UserModule>();
var instance = container.GetInstance<IUserModule>("Sql");

此代码应该返回带有注入的UserRepositorySql实例的IUserModule实例,但当然不会。

你在LightInject中对此有什么建议吗

我在Lightinject中找到了一个特性注释,可以在其中注入适当类型的一些属性,但我不太喜欢这个解决方案。

您在其他IoC容器方面有经验吗?你/哪个功能可以解决这个问题

依赖项注入-命名的依赖项

我必须同意Steven和Astrotrain的观点,但还有第三种选择。

以下示例使用参数来解析IUserRepository接口的正确实现。

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 { }

我知道Unity支持指定执行时要使用的命名实例通过ResolvedParameter类(仅起作用就像你想象中的结构):

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

然而,大多数DI框架也支持工厂方法,以及上面的例子也可以在Unity中写成:

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

在这种情况下,你告诉Unity,你希望它在任何时候都使用你的表达式它应该创建一个IUserModule实例,在该表达式中,您是自由的使用容器解析特定实例。

我怀疑后一种结构很可能与你的DI一起进行框架。