Unity:告诉容器在注册类型时始终使用接口的特定实现

Unity: tell container to always use specific implementation of an interface when registering a type

我将 运行 保留在这种情况下:假设我有接口 IFoo 和多个实现,例如 RedFoo : IFooBlackFoo : IFoo。然后我有 类 在构造函数中采用 IFoo

class BlackFooUser
{
   public BlackFooUser(IFoo foo, other_parameters_here) {...}
}

class RedFooUser
{
   public RedFooUser(IFoo foo, other_parameters_here) {...}
}

如何告诉容器像往常一样解析所有其他参数,但在构造BlackFooUser时始终使用BlackFoo,而在构造RedFooUser时始终使用RedFoo ?我知道我可以在调用 Resolve() 时使用 ParameterOverride,但我希望它在解析 Red/BlackFooUser 时相同,所以这应该进入 RegisterTypeRegisterFactory.

我可以做类似

container.RegisterFactory<RedFooUser>(c=>new RedFooUser(c.Resolve<RedFoo>(), other_parameters_here));

但这很冗长,每次我添加新参数时都必须更改。

有没有更好的方法?

UPDATE 我特别希望通过容器注册来做到这一点,而不修改所涉及的 类。这有多种原因,但归结为:

使用抽象 class 和泛型强制指定类型。

Unity 会自动解析具体类型,所以你不需要注册这些。

public abstract class FooUser<TFoo> where TFoo : IFoo
{
    private readonly TFoo _foo;
    public FooUser(TFoo foo, other parameters)
    {
        _foo = foo;
    }
}

public class BlackFooUser : FooUser<BlackFoo>
{
    public BlackFooUser (BlackFoo foo, other parameters)
        : base(foo, other parameters)
    {
    }
}

public class RedFooUser : FooUser<RedFoo>
{
    public RedFooUser (RedFoo foo, other parameters)
        : base(foo, other parameters)
    {
    }
}

下面的完整复制 - 输出是:

Constructed RedFooUser with foo 'RedFoo' and otherParameter 'I'm the other parameter' Constructed BlackFooUser with foo 'BlackFoo' and otherParameter 'I'm the other parameter'

void Main()
{
    var container = new UnityContainer()
        .RegisterInstance<string>("I'm the other parameter");

    var foo1 = container.Resolve<RedFooUser>();
    var foo2 = container.Resolve<BlackFooUser>();

}

// Define other methods, classes and n
public interface IFoo 
{
}
public class BlackFoo : IFoo { }
public class RedFoo : IFoo { }

public abstract class FooUser<TFoo> where TFoo : IFoo
{

    private readonly TFoo _foo;
    public FooUser(TFoo foo, string otherParameter)
    {
        _foo = foo;
        Console.WriteLine($"Constructed {GetType().Name} with foo '{foo.GetType().Name}' and otherParameter '{otherParameter}'");
    }
}

public class BlackFooUser : FooUser<BlackFoo>
{
    public BlackFooUser(BlackFoo foo, string otherParameter)
        : base(foo, otherParameter)
    {
    }
}

public class RedFooUser : FooUser<RedFoo>
{
    public RedFooUser(RedFoo foo, string otherParameter)
        : base(foo, otherParameter)
    {
    }
}

当你注册你的类型时,你可以给它们一个名字,然后在注入这些类型的构造函数中引用这个名字。像这样:

public class BlackFoo : IFoo
{
    public const string UNITY_KEY = "BlackFoo";
}

在您的容器注册中:

_container.RegisterType<IFoo, BlackFoo>(BlackFoo.UNITY_KEY, new ContainerControlledLifetimeManager());

在你的构造函数中:

public class BlackFooUser
{
    public BlackFooUser([Dependency(BlackFoo.UNITY_KEY)] IFoo foo)
    {
    }
}

这确保当容器进行依赖注入时,它将使用指定的 IFoo 注册。

我将 an issue 提交到 UnityContainer 存储库,Eugene Sadovoy 的回复促使我做出这个答案。

为了避免无限循环,可以将默认工厂注册为命名注册,并从默认的未命名工厂调用它,方法如下(抱歉,与问题相比,我稍微更改了名称):

container
  .RegisterType<SpecialFooUser>("stdreg")
  .RegisterFactory<SpecialFooUser>(
      c => c.Resolve<SpecialFooUser>("stdreg", 
        new DependencyOverride<IFoo>(c.Resolve<SpecialFoo>()))
    );

这可行,但看起来很冗长,所以我写了一些扩展方法和 类(总共约 100 行代码)以使其不那么冗长而更具表现力:

container.Register(
  new CustomFactory<SpecialFooUser>()
    .DependencyOverride<IFoo, SpecialFoo>());

完整源码在GitHub:https://github.com/ikriv-samples/UnityResolveOverrideFactory