我目前正在尝试在装饰器中包装一个类,并在运行时注入其中一个依赖项。我目前有一个IStorage接口,由StorageCacheDecoratorStorage实现。 StorageCacheDecorator接受IStorageStorage object takes in a Context`对象。但是,每次解析这些类时,都需要传递上下文对象。

public interface IStorage
{

}

public class Storage : IStorage
{
    public Context Context { get; }

    public Storage(Context context)
    {
        this.Context = context;
    }
}

public class StorageCacheDecorator : IStorage
{
    public IStorage InnerStorage { get; }

    public StorageCacheDecorator(IStorage innerStorage)
    {
        this.InnerStorage = innerStorage;
    }
}

public class Context
{
}


我省略了实现细节,下面的测试给出了我的问题的示例

    [Test]
    public void ShouldResolveWithCorrectContext()
    {
        var context = new Context();

        var container = new UnityContainer();

        container.RegisterType<Storage>();

        container.RegisterType<IStorage>(
            new InjectionFactory(c => new StorageCacheDecorator(
                c.Resolve<Storage>())));

        var resolve = container.Resolve<IStorage>(new DependencyOverride<Context>(context));

        Assert.That(resolve, Is.TypeOf<StorageCacheDecorator>());

        var cacheDecorator = ((StorageCacheDecorator)resolve);
        Assert.That(cacheDecorator.InnerStorage, Is.TypeOf<Storage>());

        var storage = ((Storage)cacheDecorator.InnerStorage);
        Assert.That(storage.Context, Is.SameAs(context));
    }


但是,如果我们删除装饰器,则测试通过

    [Test]
    public void ShouldResolveWithCorrectContext1()
    {
        var context = new Context();

        var container = new UnityContainer();

        container.RegisterType<IStorage, Storage>();

        var resolve = container.Resolve<IStorage>(new DependencyOverride<Context>(context));

        Assert.That(resolve, Is.TypeOf<Storage>());

        Assert.That(((Storage)resolve).Context, Is.SameAs(context));
    }


如何使InjectionFactory尊重DependencyOverride

最佳答案

首先,我非常确定您(今天-我)偶然发现的是Unity中的错误。

通过this excellent article的示例,我设法诊断出了问题,并从中得到了BuilderStrategy的示例。在我用此扩展替换了InjectionFactory之后,它在某些情况下有效,而在其他情况下则无效。

经过一番调查,似乎Unity中的所有分辨率都针对相同的Container及其配置工作,并且所有DependencyOverrides都沿着调用的方向在IBuilderContext对象(包含resolverOverrides的一组policies中)中拖动>由DependencyOverrides构造。 IBuilderContext提供了一个GetResolverOverride(Type)方法,该方法允许实现获取给定Type的值替代。

因此,如果您明确要求IBuilderContext.GetResolverOverride替代存储Context,则将获得所需的Same-Context-Object。

但是,如果您尝试询问Container本身,则将获得按照标准规则解析的Context对象。并不是在分辨率上被覆盖。

这就是为什么要在InjectionFactory委托中尝试container.Resolve(..)的原因,如下所示:

    container.RegisterType<IStorage>(
        new InjectionFactory(c => new StorageCacheDecorator(
            c.Resolve<Storage>())));  // <-- this C is Container


将无法满足覆盖,因为..容器不了解覆盖!

它必须是:

    container.RegisterType<IStorage>(
        new InjectionFactory(c => new StorageCacheDecorator(
            builderContext.Resolve<Storage>())));


如果已实现,则可以访问GetResolverOverride并使用正确的覆盖构建适当的存储。但是,不存在这样的方法,更糟糕的是,在此刻,您无法访问IBuilderContext-InjectionFactory无法将其提供给您。可能只有分机和朋友可以访问。

有趣的事实:IBuilderContextGetResolverOverride,但没有任何.Resolve。因此,如果您要从该文章中获取代码,并且使用该文章中的PreBuildUp来执行自己的解析逻辑,则必须使用Container(并在解析器覆盖上失败),或者进入检查所有内容并手动执行构造实例所需的所有子分辨率的工作。 IBuilderContext为您提供了一个漂亮的NewBuildUp()方法,这似乎是一个很好的捷径,但是..它使用基本Container,并且不会转发解析器覆盖。

看到它有多么复杂和不直观,以及意外地将那组resolveroverrides和回退到原始上下文有多么容易,我很确定InjectionFactory是EVIL / BUGGED / MISDESIGNED / etc:它为您提供了“ c”,您的主Container实例,因此您就无法正确解决有关覆盖的参数。代替该主容器,我们应该获取某种派生容器或额外的生成器对象等。

使用本文中找到的代码,我能够破解一些使用此GetResolverOverride初始化我的新对象实例的东西,但这绝不是通用的,并且完全不可重用(我只是称呼.GetResolverOverride要获取值并将其直接传递到new MyObject(value) ..但是,天哪,这很糟糕,但是,我需要将其作为测试设置的一部分,因此我可以忍受直到代码重构为止。

现在,让我们回到您的情况。显然,您可以做类似的事情,但是事实证明,在装饰器的情况下,有一种更简单的方法:只需摆脱InjectionFactory。您最初的实例初始化代码可能更复杂,但是如果有任何可能,它实际上实际上与示例中的代码一样简单:

    container.RegisterType<IStorage>(
        new InjectionFactory(c =>
            new StorageCacheDecorator(   // <- NEW &
                 c.Resolve<Storage>()    // <- RESOLVE
        )));


您应该实际上使用了声明性方式,而不是命令性的Bug注入法:

    container.RegisterType<IStorage, StorageCacheDecorator>(
          new InjectionConstructor(
               new ResolvedParameter<Storage>()
          ));


最终效果是完全相同的:创建新对象,调用单参数构造函数,并解析Storage并将其用作该参数。我在您的测试用例中进行了尝试,并且效果很好。

除了使用'ResolvedParameter',还可以使用直接对象实例,例如:

    container.RegisterType<IStorage, StorageCacheDecorator>(
          new InjectionConstructor(
               "foo", 5, new Shoe()
          ));


但是,就像您原始示例中的new StorageDecorator一样,InjectionConstructor需要获取构造函数的所有参数,并且显然,当将来构造函数参数发生更改时,我们不会收到编译时错误。它也比InjectionFactory有更多限制,因为它需要预先获取所有指定的参数。

更讨厌Unity的原因..

关于c# - Unity IoC InjectionFactory不遵守DependencyOverride,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42348085/

10-11 15:52