0

考虑下面的代码示例:

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

4 回答 4

2

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

命名法

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

Plugged/concrete type - 插件类型的实现

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

SM中一般有两种手动注册。您可以使用Add()Use()方法注册依赖项。

有什么区别?

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

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

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

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

于 2014-12-08T19:03:25.933 回答
0

How to map same interface to different ConcreteClasses with StructureMap?

Look at the above. Most DI frameworks use hints presented in the form of names/attributes to inject the appropriate concrete classes at run time.

Look at the below to

StructureMap: Choose concrete type of nested dependency

于 2014-10-24T09:15:01.407 回答
0

在某些情况下,对同一个接口进行多个实现是很有意义的,但并非在所有情况下都如此。始终确保您没有违反Liskov 替换原则(LSP)。这里有两种情况,一种可以使用多个实现,另一种则不能。

比如说你有一个ILogger抽象FileLoggerSqlLogger实现。在大多数情况下,您希望将内容记录到数据库中,但在系统的某些部分,您明确希望记录到文件中,因为系统这部分中的日志错误是由另一个系统拾取的,该系统没有访问数据库。尽管您应该始终问自己是否记录了太多,但只要交换实现不影响ILogger抽象的消费者,从 LSP 的角度来看,您就可以了。

但是,假设您的应用程序与两个数据库通信,每个数据库都有自己的模式。除此之外,您还有一个IUnitOfWork抽象,用于与数据库进行通信。基于这个抽象,您实现了OrdersUnitOfWork与 Orders 数据库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>());

这里根据消费者的命名空间确定注入哪个实现。当然,您可以考虑各种规则来确定要注入的内容。

于 2014-10-24T11:46:42.470 回答
0

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

于 2015-02-23T18:33:53.487 回答