1

我正在處理一個大型的傳統C#應用程序,並且分配給我的任務是刪除靜態工廠類ServiceLocator.GetObject<T>()的所有用法,並全部替換爲構造函數注入的依賴項。在擴展方法中需要工廠類

大部分情況下,這很簡單,但是在應用程序代碼庫中有大約50個情況,這有點棘手。例如,Servicelocator用於靜態類或擴展方法,甚至是WPF MarkupExtension !.

例如,如果您遇到像這樣的代碼片段,您會怎麼做? (除了哭)

public static class MyExtensions 
{ 
    private static ISingletonServiceOne _ServiceOne = null; 
    private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 

    public static SummaryHeader GetBannerSummary(this IModel rfq, object requester) 
    { 
     Guard.ArgumentNotNull(rfq, "rfq"); 
     Guard.ArgumentNotNull(requester, "requester"); 

     if (_ServiceOne == null) 
     { 
      _ServiceOne = ServiceLocator.GetService<ISingletonServiceOne>(requester); 
      Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne"); 
     } 

     return _ServiceOne.GetBannerSummary(rfq); 
    } 

在上述的ServiceLocator.GetObject()方法已經以擴展方法被用在IModel來定位一個單註冊的服務和關於使用IModel執行的方法。

的問題是:

  • 是否有任何模式/做法,以避免這樣的事情 - 在一個靜態類,值轉換器或擴展方法
  • 需要一個DI容器是否有任何模式/在DI中處理循環依賴的實踐?
  • 如果在良好的代碼和交付時間之間存在折衷,那麼您會在上面做什麼?

我想移動GetBannerSummary()方法進行擴展,只有IModel在這種情況下,但是,(不要笑)也有在ValueConverters(WPF)和MarkupExtensions所使用的相同的服務定位的情況下, :0

您的意見/建議表示讚賞

回答

2

我會永遠使用服務定位器的唯一時間是在靜態方法,如擴展方法和IValueConverters,不幸的是,確實沒有任何其他獲取依賴關係的好方法。

唯一的(稍微)更好的解決方案是將ServiceLocator調用移出到延遲加載的屬性,以便可以在單元測試期間注入依賴項。

然而,在你的情況下,因爲你有一個請求者屬性被傳遞給GetService,所以你最好需要添加一個IServiceOneFactory依賴項,你可以傳遞你的請求者對象。所以:

public interface IServiceOneFactory 
{ 
    ISingletonServiceOne Create(object requester); 
} 

public static class MyExtensions 
{ 
    public static IServiceOneFactory ServiceOneFactory 
    { 
     get 
     { 
      if(_ServiceOneFactory==null) 
       _ServiceOneFactory = ServiceLocator.GetService<IServiceOneFactory>(); 
      return _ServiceOneFactory; 
     } 
     set { _ServiceOneFactory = value; } 
    } 

    private static IServiceOneFactory _ServiceOneFactory = null; 
    private static ISingletonServiceOne _ServiceOne = null; 
    private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 

    public static SummaryHeader GetBannerSummary(this IModel rfq, object requester) 
    { 
     Guard.ArgumentNotNull(rfq, "rfq"); 
     Guard.ArgumentNotNull(requester, "requester"); 

     if (_ServiceOne == null) 
     { 
      _ServiceOne = ServiceOneFactory.Create(requester); 
      Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne"); 
     } 

     return _ServiceOne.GetBannerSummary(rfq); 
    } 
} 
+0

嗨馬克,感謝您的答案 - 我編輯了外部請求者作爲ServiceLocator的實現實際上沒有做任何事情!同意你的預後。這是一個相當令人震驚的意大利麪條網絡,我在這裏處理,所以任何建議打破它歡迎:) –

+0

嗨安德魯,不客氣 - 我們都必須經歷它!我認爲你所做的一切都是正確的,但我在靜力學,擴展等方面看不到有關ServiceLocator的方法。如果您已將所有依賴關係保持爲模塊化,那麼這是一個必要的弊端。 –

+0

大的重構正在進行中,因爲在另一個項目中需要重新使用大塊代碼作爲棱鏡「模塊」。當我聽到這個消息時,我的反應是「大聲笑」。無論如何,在ServiceLocator的120個用例中,我已經下降到了60個。我認爲我可以調整更多,但是如果沒有「易用」解決方案,可能會留下一些。非常感謝 –

2

是否注入IServiceX到你的類,而不是使用一個靜態訪問類的選項?也許使GetBannerSummary方法成爲實現IModel的抽象基類的一部分?

當您不控制對象實例化時,DI不會飛行。 WPF中的觸發器,行爲或標記擴展屬於該類別。沒有選擇在那裏使用ServiceLocator。

+0

嗯,因爲我懷疑沃森...似乎我將不得不做很多工作來解決這個意大利麪條 –