【问题标题】:Simplejector Equivalent of StructureMap's Container.With().EqualTo()与 StructureMap 的 Container.With().EqualTo() 等效的 Simplejector
【发布时间】:2012-12-21 20:27:43
【问题描述】:

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

【问题讨论】:

  • 你能举个例子说明你想要达到的目标吗?
  • @Steven 我编辑包含一个示例
  • 似乎您正在尝试从容器中解析实体,这不是常见的事情,如Mark Seemann explains here on Stackoverflow

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


【解决方案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>()
        );
    }
}

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

因此,一般而言,我的建议是,当您的目标是进行自动装配(容器为您注入所有依赖项)时,避免在同一个构造函数中将原始值(例如 int 和字符串)与服务依赖项混合。尽管某些容器为注册和解析包含原始值的类型提供了更多支持,但它总是导致需要更多维护且更脆弱的 DI 配置。

请尝试以下任一方法:

  1. 将原始值包装在可以注入的类中。即注入 IConnectionManager 而不是 string connectionString
  2. 将原始值提升为属性。借助 Simple Injector,您可以执行 explicit property injection(通过使用 RegisterInitializer 注册初始化委托),从而实现编译时可验证的接线。

此规则的唯一例外是当您有某种约定优于配置方法时,您可以自动检测某种原始值作为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&lt;T&gt; 代表:

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

在这种情况下,您的应用程序逻辑可以依赖于可以注入构造函数的Func&lt;int, SomeObject&gt; 委托。这消除了很多仪式感,缺点是在应用程序的设计中失去了一些表现力,因为Func&lt;int, SomeObject&gt; 不像SomeObject Create(int someValue) 那样表现力。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 2011-02-20
    相关资源
    最近更新 更多