繁体   English   中英

DI框架如何解决具有多个配置的同一接口的依赖性?

[英]How DI frameworks resolve dependency for same Interface with multiple configuration?

考虑下面的代码示例:

public interface IMyInterface
{
   void SetName(string name);
   string GetName();
}

public class MyInterfaceImplementor1 : IMyInterface
{
   protected string Name { set; get; }

   public void SetName(string name)
   {
      this.Name = name;
   }

   public virtual string GetName()
   {
      return this.Name;
   }
}

public class MyInterfaceImplementor2 : MyInterfaceImplementor1
{
   public override string GetName()
   {
      return String.Format("Hello! {0}", base.Name);
   }
}

以及为此的DI配置:(提供了StructureMap代码段)

ObjectFactory.Configure(x => 
{
   x.For<IMyInterface>()
    .Use<MyInterfaceImplementor1>();
});

ObjectFactory.Configure(x =>
{
   x.For<IMyInterface>()
    .Use<MyInterfaceImplementor2>();
});  

说,在我的代码中,有时使用MyInterfaceImplementor1,而有时使用MyInterfaceImplementor2 我的问题是,DI框架(StructureMap或任何其他框架)将如何解决上述配置? 另外,它将如何确定在哪里返回MyInterfaceImplementor1的实例以及何时在MyInterfaceImplementor2上返回相同的实例 或者我在这里做错了吗?

在您的情况下,StructureMap将解析MyInterfaceImplementor2 StructureMap存储所有注册,但解析使用Use()方法注册的最后一个注册。

命名法

插件类型 -要注入的类型,在大多数情况下,它是一个接口,但是SM还会解析具体的类型(即使您尚未注册它们)

插入/混凝土类型 -插件类型的实现

c.For<IPluginType>().Use<ConcreteType>();

通常,在SM中,有2种类型的手动注册。 您可以使用Add()Use()方法注册依赖项。

有什么区别?

  • Add()只是在插件系列中注册具体类型
  • Use()注册并将其设置为解析插件类型时要使用的默认注册

对于Add(),有一个约定,当您只有一个注册时,它将被解析。 如果存在多个注册容器,则在尝试解析插件类型时会抛出StructureMapException

csprabala答案很好地解释了应如何在StructureMap中注册多个实现。

当我有多种插件类型的实现时,我总是向Add()方法注册,如果有默认实现,我就向Use()注册它。

如何使用StructureMap将同一接口映射到不同的ConcreteClass?

看上面。 大多数DI框架使用以名称/属性形式表示的提示在运行时注入适当的具体类。

看下面

StructureMap:选择嵌套依赖项的具体类型

在某些情况下,具有同一接口的多个实现非常有意义,但并非在所有情况下都如此。 始终确保您没有违反Liskov替代原则 (LSP)。 这有两种情况,一种可以使用多种实现,而另一种则可以。

假设您有一个ILogger抽象和FileLoggerSqlLogger实现。 在大多数情况下,您希望将内容记录到数据库中,但是在系统的某些部分中,您明确地希望记录到文件中,因为该系统这部分中的日志错误是由没有访问数据库。 尽管您应该经常问自己是否记录过多日志 ,但是只要交换实现不会影响ILogger抽象的使用者,从LSP的角度来看,您就可以了。

但是,假设您的应用程序与两个数据库通信,每个数据库都有自己的架构。 最重要的是,您具有IUnitOfWork抽象,用于与数据库进行通信。 在此基础上抽象你实现OrdersUnitOfWork与订单数据库通信,以及CrmUnitOfWork与CRM数据库进行通信。 显然,系统的某些部分需要使OrdersUnitOfWork起作用,而其他部分则需要CrmUnitOfWork 在这种情况下,您将破坏LSP,因为您不能在没有使用者注意的情况下简单地交换实现。 不可以,当为消费者提供错误的实现时,它将完全中断,因为数据库架构的完全不同。 这违反了LSP。

因此,在后一种情况下,问题出在应用程序的设计中,可以通过为每个数据库提供自己的抽象来解决。 例如: IOrdersUnitOfWorkICrmUnitOfWork

如果执行这种注册,则它完全取决于您使用的DI库,无论它返回第一个还是最后一个。 这通常是很不直观的,因此,很容易造成配置错误。

因此,DI库Simple Injector不允许进行此类注册。 正如我们在此处解释的那样,Simple Injector的设计人员发现此类API是设计缺陷。 相反,Simple Injector会强制您为给定密钥(在您的情况下为IMyInterface )进行一次注册或注册事物的集合。 这样可以避免将库返回给您的所有混淆。

万一您需要根据其使用者确定要注入的实现,Simple Injector允许您进行基于上下文的注入 例如:

container.Register<MyInterfaceImplementor1>();
container.Register<MyInterfaceImplementor2>();

container.RegisterWithContext<IMyInterface>(context =>
    context.ImplementationType.Namespace.Contains("Administrator")
        ? container.GetInstance<MyInterfaceImplementor1>()
        : container.GetInstance<MyInterfaceImplementor2>());

在此,根据使用者的名称空间确定要注入的实现。 您当然可以考虑各种规则来确定要注入的内容。

如果有多个注册,StructureMap会发生异常,因为它不会尝试猜测您想要哪个注册-我一直坚持这一决定。 其他一些IoC容器将使用第一个注册的容器或最后一个注册的容器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM