配置Unity以解析具有修饰的依赖项的类型,该修饰的依赖项的参数随注入的类型而异 [英] Configuring Unity to resolve a type that takes a decorated dependency that has a parameter that varies with the type into which it is injected

查看:70
本文介绍了配置Unity以解析具有修饰的依赖项的类型,该修饰的依赖项的参数随注入的类型而异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个非常简单的装饰器模式方案,复杂的是装饰的类型具有一个构造函数参数,该参数取决于要注入的类型。

This is a fairly straight forward decorator pattern scenario, with the complication that the decorated type has a constructor parameter that is dependent on the type into which it is being injected.

我有一个这样的接口:

interface IThing
{
    void Do();
}

以及类似的实现:

class RealThing : IThing
{
    public RealThing(string configuration)
    {
        ... implementation ...
    }

    public void Do()
    {
        ... implementation ...
    }
}

和这样的装饰器:

class DecoratingThing : IThing
{
    IThing _innerThing;

    public DecoratingThing(IThing thing)
    {
        _innerThing = thing;    
    }

    public void Do()
    {
        _innerThing.Do();
    }
}

最后,我有些类型需要 IThing ,称为 Depender1 Depender2 等。

Finally, I have some types that require an IThing, called Depender1, Depender2 etc..

class DependerX()
{
    public DependerX(IThing thing)
    {
        ... implementation ...
    }
}

我要配置IOC容器来解析 DependerX 的实例,以便为它们注入 RealThing 并装饰有 DecoratingThing 重要:每个 DependerX 类型需要不同的配置传递给其 RealThing 的构造函数,在每种情况下都说 ConfigX。例如IoC容器完成的工作可能是:

I want to configure an IOC container to resolve instances of DependerX such that they are injected with RealThing decorated with a DecoratingThing. Important: Each DependerX type requires a different value of configuration to be passed to the constructor of its RealThing, say "ConfigX" in each case. e.g. The work done by the IoC container might be:

new Depender1(new DecoratingThing(new RealThing("Config1")));
new Depender2(new DecoratingThing(new RealThing("Config2")));

...等等。

在Unity中,这配置起来很笨拙,因为我必须在装饰器中与装饰器混合使用:

In Unity, this seems quite clunky to configure as I have to mix in the decorator with the decorated:

container.RegisterType<IThing, DecoratingThing>("ConfigX",
    new InjectionFactory(container => new DecoratingThing(new RealThing("ConfigX"));

container.RegisterType<DependerX>(
    new InjectionConstructor(new ResolvedParameter<IThing>("ConfigX");

然后重复,很好地违反了DRY ,对于每个 DependerX

And repeat, violating DRY nicely, for each DependerX.

我想做的就是消除嵌入 RealThing IThing 的每个命名注册中构造 DecoratingThing 的构造-并只声明一次装饰,例如,这样,如果将来需要更改装饰,那么重新配置就更容易了,我想到的最好的就是这种registra辅助方法

What I'd like to do is remove the need to embed the construction of RealThing in the construction of DecoratingThing in each named registration of IThing - and declare the decoration just once. This is so, for example, that if the decoration needs to change in future, it's easier to reconfigure. The best I came up with is this helper method for registration:

void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
    container.RegisterType<TDepender>(new InjectionConstructor(
        new ResolvedParameter<IThing>(config)));
    container.RegisterType<IThing, DecoratingThing>(config,
        new InjectionFactory(c => new DecoratingThing(new RealThing(config))));
}

这至少消除了重复,但是我仍然必须嵌入 DecoratingThing 中的 RealThing -例如,这意味着我无法独立更改其寿命。我无法再次注册 IThing 来执行此操作,因为我已经用完该接口的名称注册了。如果我想这样做,我必须引入另一组这样的命名实例:

This removes repetition at least, but I still have to embed the construction of the RealThing inside the DecoratingThing - this means I can't vary their lifetimes independently for example. I can't register IThing again to do this because I've used up my registration of that interface for the name. If I want to do that I have to introduce another set of named instances like so:

void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
    string realConfig = "Real" + config;

    container.RegisterType<TDepender>(new InjectionConstructor(
        new ResolvedParameter<IThing>(config)));
    container.RegisterType<IThing, DecoratingThing>(config,
        new InjectionFactory(c => new DecoratingThing(
            container.Resolve<IThing>(realConfig))));
    container.RegisterType<IThing, RealThing>(realConfig,
        new ContainerControlledLifetimeManager(),
        new InjectionConstructor(config));
}

这真的是最好的选择吗?对于那些后悔的人来说,感觉很复杂,而且可能很难。其他IoC容器是否有一种引人注目的方法来解决这种情况?由于对每个DependerX重复了注入工作方式,有没有办法只在顶层( DependerX )级别使用命名实例?

Is this really the best option? It feels complex and potentially hard for those that will come after to grok. Do other IoC containers have a compelling way to cover this scenario? Since the pattern for how injection works is repeated for each DependerX, is there a way to only use a named instance at the top (DependerX) level?

还有其他评论吗?

推荐答案

类设计本身似乎是合理的。这是一个基于基于公约的容器配置,基本上可以做到这一点:

The class design itself seems reasonable. Here's a convention-based container configuration that basically does this:

public class MyConventions : UnityContainerExtension
{
    protected override void Initialize()
    {
        var dependers = from t in typeof(IThing).Assembly.GetExportedTypes()
                        where t.Name.StartsWith("Depender")
                        select t;

        foreach (var t in dependers)
        {
            var number = t.Name.TrimStart("Depender".ToArray());
            var realName = "Real" + number;
            var decoName = "Deco" + number;
            var config = "Config" + number;
            this.Container.RegisterType<IThing, RealThing>(realName, 
                new InjectionConstructor(config));
            this.Container.RegisterType<IThing, DecoratingThing>(decoName,
                new InjectionConstructor(
                    new ResolvedParameter<IThing>(realName)));
            this.Container.RegisterType(t,
                new InjectionConstructor(
                    new ResolvedParameter<IThing>(decoName)));
        }
    }
}

此配置将自动添加所有与上述谓词相匹配的类,因此,一旦设置好,就可以添加更多类(例如 Depender4 Depender5

This configuration will automatically add all classes that match the above predicate, so once you've set it up, you can just add more classes (like Depender4 or Depender5) without revisiting the container configuration at all.

上面的配置满足以下单元测试:

The above configuration satisfies these unit tests:

[Fact]
public void ContainerCorrectlyResolvesDepender1()
{
    var container = new UnityContainer().AddNewExtension<MyConventions>();
    var actual = container.Resolve<Depender1>();

    var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
    var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
    Assert.Equal("Config1", thing.Configuration);
}

[Fact]
public void ContainerCorrectlyResolvesDepender2()
{
    var container = new UnityContainer().AddNewExtension<MyConventions>();
    var actual = container.Resolve<Depender2>();

    var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
    var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
    Assert.Equal("Config2", thing.Configuration);
}

[Fact]
public void ContainerCorrectlyResolvesDepender3()
{
    var container = new UnityContainer().AddNewExtension<MyConventions>();
    var actual = container.Resolve<Depender3>();

    var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
    var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
    Assert.Equal("Config3", thing.Configuration);
}

这篇关于配置Unity以解析具有修饰的依赖项的类型,该修饰的依赖项的参数随注入的类型而异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆