簡體   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