SimpleMapor等效于StructureMap的Container.With()。EqualTo()

时间:2012-12-21 20:27:44

标签: .net dependency-injection inversion-of-control ioc-container simple-injector

使用Simple Injector,是否有相当于StructureMap的Container.With("CustomerId").EqualTo(100).GetInstance<Customer>()(通过反射查找属性CustomerId)?

1 个答案:

答案 0 :(得分:1)

Simple Injector中没有与此相同的内容。原因是这样的构造通常会导致Service Locator anti-pattern,并且会导致使用魔术字符串导致的脆弱配置。

因此,一般(独立于容器)的建议是使用抽象工厂(正如Mark Seemann解释here)。应用程序可以依赖抽象工厂而不是使用容器。您将责任移交给工厂。仍然需要在工厂内创建依赖项。此示例显示了一个解决方案:

// Part of Composition Root
private class SomeObjectFactory : ISomeObjectFactory
{
    private readonly Container container;

    public SomeObjectFactory(Container container)
    {
        this.container = container;
    }

    public SomeObject Create(int someValue)
    {
        return new SomeObject(someValue,
            this.container.GetInstance<IOtherDependency>()
        );
    }
}

虽然这有效,但每次Create的构造函数更改时都必须更改SomeObject方法。

因此,一般来说,当你的目标是进行自动连接(容器为你注入所有依赖关系)时,我的建议是避免将原始值(例如int和string)与服务依赖关系混合在同一个构造函数中。虽然一些容器具有更多的支持,用于注册和解析包含原始值的类型,但它总是导致DI配置需要更多维护并且更脆弱。

相反,尝试:

  1. 将原始值包装在可以注入的类中。即注入IConnectionManager而不是string connectionString
  2. 向物业推广原始价值。使用Simple Injector,这允许您执行explicit property injection(通过使用RegisterInitializer注册初始化委托),这将启用编译时可验证的连接。
  3. 此规则的唯一例外是,当您使用某种约定优于配置方法时,可以使您自动检测某种原始值shown here。但是,我认为你必须好好看看你的设计。当将相同的原语(配置)值注入到多个服务中时,您可能缺少抽象。在这种情况下,请应用#1。

    然而,这个建议特别适用于配置值。您正在处理运行时值。但是,在处理运行时值时,仍然可以使用属性:

    // Part of Composition Root
    private class SomeObjectFactory : ISomeObjectFactory
    {
        private readonly Container container;
    
        public SomeObjectFactory(Container container)
        {
            this.container = container;
        }
    
        public SomeObject Create(int someValue)
        {
            var instance = this.container.GetInstance<SomeObject>();
            instance.SomeValue = someValue;
            return instance;
        }
    }
    

    通过将SomeValue提升为属性,我们允许容器在SomeObject上使用自动连接,并在注入someValue的运行时值时启用编译时支持。

    然而,使用这样的工厂可能看起来有点冗长,而其他人可能会注入Func<T>代表:

    container.RegisterSingle<Func<int, SomeObject>>(someValue =>
    {
        var instance = this.container.GetInstance<SomeObject>();
        instance.SomeValue = someValue;
        return instance;
    });
    

    在这种情况下,应用程序逻辑可以依赖于可以注入构造函数的Func<int, SomeObject>委托。这消除了大量的争议,但在应用程序的设计中失去了一些表现力的缺点,因为Func<int, SomeObject>SomeObject Create(int someValue)的表达式相比要少得多。

    然而,在您的特定情况下,您似乎正在解析实体。这可能不是最好的事情,正如Mark Seemann解释here。此外,让容器创建使用运行时ID提供的实体对我来说似乎很奇怪,因为通常会从数据库中检索具有Id的实体。

    您可能正在应用域驱动设计,这就是您在实体上需要这些服务依赖性的原因。考虑使用方法注入,而不是进行构造函数或属性注入。只需将实体方法所需的依赖项添加为方法参数即可。然后,您可以在从command handler调用该方法时传递这些依赖项。在这种情况下,命令处理程序仍然可以使用普通的构造函数注入。当我第一次看到这个(我相信这是Jimmy Nilsson的演讲)时,我觉得它与我所知道的一切都有冲突。但是,过了一段时间后,我开始发现这实际上是一个非常好的解决方案(针对这种特殊情况)。