DI框架如何解决多个配置的相同接口的依赖关系?

时间:2014-10-24 09:02:52

标签: c# dependency-injection structuremap3

考虑下面的代码示例:

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 的相同位置?或者我在这里做错了什么?

4 个答案:

答案 0 :(得分:2)

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

命名法

插件类型 - 要注入的类型,在大多数情况下它是一个接口,但SM也解决了具体的类型(即使你还没有注册它们)

已插入/具体类型 - 插件类型

的实现
c.For<IPluginType>().Use<ConcreteType>();

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

有什么区别?

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

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

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

当我有多个插件类型的实现时,我总是使用 Add()方法注册,如果有默认实现,我使用 Use()

答案 1 :(得分:0)

How to map same interface to different ConcreteClasses with StructureMap?

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

请看下面的

StructureMap: Choose concrete type of nested dependency

答案 2 :(得分:0)

在某些情况下,拥有相同接口的多个实现是很有意义的,但并非在所有情况下都是如此。始终确保您没有违反Liskov Substitution Principle(LSP)。这里有两种情况,一种是使用多种实现方式,另一种方式不是。

比如说,你有ILoggerFileLogger实现的SqlLogger抽象。在大多数情况下,您希望将内容记录到数据库中,但是在系统的某些部分中,您明确地希望记录到文件,因为系统的这一部分中的日志错误由另一个没有的系统拾取访问数据库。虽然你应该总是问问自己whether or not you're logging too much,只要交换实现并不影响ILogger抽象的消费者,从LSP的角度来看,你没事。

但是,我们假设您的应用程序与两个数据库通信,每个数据库都有自己的架构。除此之外,您还有一个IUnitOfWork抽象,用于与数据库通信。基于此抽象,您实现了OrdersUnitOfWork以与Orders数据库进行通信,并CrmUnitOfWork与CRM数据库进行通信。显然,系统的某些部分需要OrdersUnitOfWork才能正常工作,而其他部分则需要CrmUnitOfWork。如果是这种情况,那么您正在打破LSP,因为您无法在没有消费者注意的情况下交换实施。不,当向消费者提供错误的实现时,它将完全破坏,因为数据库模式是完全不同的。这违反了LSP。

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

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

因此,DI库Simple Injector不允许这类注册。 Simple Injector的设计者在我们解释here时发现这样的API是一个设计缺陷。相反,Simple Injector强制您为给定键(在您的情况下为IMyInterface)进行单次注册或注册事物集合。这可以防止图书馆为您返回的内容混淆不清。

如果您需要确定要注入的实现,基于其使用者,Simple Injector允许您执行context based injection。例如:

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

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

这里要注入的实现是基于使用者的命名空间确定的。您可以考虑所有类型的规则来确定要注入的内容。

答案 3 :(得分:0)

StructureMap将会出现异常情况,因为如果有多个注册,它将不会尝试猜测你想要哪一个 - 我支持这个决定到今天。其他一些IoC容器将使用第一个注册的或最后一个注册的。