构造函数在 Unity 容器中注入多个实现

本文关键字:注入 实现 Unity 构造函数 | 更新日期: 2023-09-27 18:34:53

对于以下接口和类,如何使用 Unity 容器流畅地(以编程方式(连接它,以便FooController获取ARepository的实例,BarController通过构造函数注入获取BRepository的实例?

public interface IRepository
{
}
public class ARepository : IRepository
{
}
public class BRepository : ARepository
{
}
public class FooController
{
    public FooController(IService service, IRepository repository)
    {
    }
}
public class BarController
{
    public BarController(IService service, IRepository repository)
    {
    }
}

构造函数在 Unity 容器中注入多个实现

可以在注册时通过告诉每个控制器注册如何解析其构造函数参数来实现此目的。

container.RegisterType<FooController>(new InjectionConstructor(
    new ResolvedParameter<IService>(), new ResolvedParameter<ARepository>());
container.RegisterType<BarController>(new InjectionConstructor(
    new ResolvedParameter<IService>(), new ResolvedParameter<BRepository>());

我强烈建议不要像海报中建议的那样为每种类型创建一个专用/本地/注入的 UnityContainer。

这可以通过两种方式来实现。

一种是按照TylerOhlsen的建议,具体定义注册时间的解决。这是一个很好的解决方案,但如果您稍后将 ARepository 和 BRepository 注册为 IRepository 的实现,您仍然需要处理同一接口有两个实现的事实,如果第三类有一天需要实现 IRepository,而没有在注册中明确定义它,它将获得一个不可预测的实例。

第二种选择稍微安全一些,是注册一家工厂进行独立保险。最简单的示例是使用字符串作为键,如下所示:

// Create a resolver(abstract factory) for the IRepository interface type
var resolver = myContainer.Resolve<Func<IRepository>>();
// ... other code here...
// Register mappings for the IRepository interface to appropriate concrete types
myContainer.RegisterType<IRepository, ARepository>("A");
myContainer.RegisterType<IRepository, BRepository>("B");

然后在 FooController 和 BarController 的实现中通过注入接收 func 工厂并选择正确的实例。

public class FooController
{
   IRepository repository;
   public FooController(IService service, Func<IRepository> repositoryFactory)
   {
       repository = repositoryFactory("A");
   }
}

您可以在此处阅读有关此内容的更多信息:http://msdn.microsoft.com/en-us/library/ff660854%28v=pandp.20%29.aspx

可以使用名称注册类型,然后在目标类的 Dependency 属性中使用该名称:

// Register mappings for the IRepository interface to appropriate concrete types
myContainer.RegisterType<IRepository, ARepository>("A");
myContainer.RegisterType<IRepository, BRepository>("B");

然后在 FooController 和 BarController 中,使用 Dependency 属性声明您需要的实现:

public class FooController
{
    public FooController(IService service, [Dependency("A")] IRepository repository)
    {
    }
}
public class BarController
{
    public BarController(IService service, [Dependency("B")] IRepository repository)
    {
    }
}

您可以在ARepositoryBRepository中使用public const string,而不是在RegisterTypeDependency中使用"A""B"

一种可能性是在解析IRepository时注册UnityContainer要解析的特定类型:

IUnityContainer container = new UnityContainer();
container.RegisterType<IRepository, BRepository>(new ContainerControlledLifetimeManager());

使用此选项取决于您是否需要对某些上下文中解析的类型进行更精细的控制 - 如果这样做,您可以考虑使用本地IUnityContainer实例并改用RegisterInstance()

//Assumes container is instantiated and already populated with other instances/type mappings.
IUnityContainer childContainer = container.CreateChildContainer();
container.RegisterInstance<IRepository>(new BRepository(), new ContainerControlledLifetimeManager());

JoefGoldstein 让我大部分时间都得到了帮助,但在尝试这样做时我遇到了一些错误。

我必须使用命名依赖项注册我的类,并使用 InjectionFactory 类来解决依赖项。

// register interfaces to implementations with a named dependency 
myContainer.RegisterType<IRepository, ARepository>("A");
myContainer.RegisterType<IRepository, BRepository>("B");
// register Injection factory to resolve the dependency
container.RegisterType<Func<string, IRepository>>(
            new InjectionFactory(c =>
            new Func<string, IRepository>(name => c.Resolve<IRepository>(name)))
);

然后在我的控制器中

public class FooController
{
   IRepository repository;
   public FooController(IService service, Func<string, IRepository> repositoryFactory)
   {
       repository = repositoryFactory("A");
   }
}