2017-08-01 134 views
2

我在依賴鏈中有許多服務類(服務A依賴於服務B,依賴於服務C等);他們的行爲由一個通用參數(CountryCode)確定,這個參數可能是在運行時定義的支持國家。注意:參與者可以縮放成多個實例(不同的線程),並且一個事件只能由一個參與者處理,下面的服務是暫時的(儘管如果需要,我可以考慮改變它)。如何通過依賴注入將狀態傳遞到依賴鏈中

目前,我有一些像這樣:

//This application flow starts off with this class 
public class ActorExample 
{ 
    private IServiceOne _serviceOne; //Has dependent service 

    public async Task ProcessAsync(Event event) 
    { 
     //This value needs to be passed to _serviceOne and any children 
     //but we only know its value at runtime. 
     event.CountryCode; 
    } 
} 

public class ServiceOne : IServiceOne 
{ 
    private IServiceTwo _serviceTwo; //Has another nested dependency 

    //Implementation here varies depending on event.CountryCode 
    public async Task DoSomething() 
} 

public class ServiceTwo : IServiceTwo 
{ 
    //Implementation here varies depending on event.CountryCode 
    public async Task DoSomething() 
} 

我想我也許可以使用泛型與服務,從而通過國家代碼如下所示:

public class ServiceTwo<TCountryCode> : IServiceTwo<TCountryCode> 

但是因爲我們只有在運行時這個值是不可能的,特別是在注入服務時。

另一種解決方案是用一個依賴COUNTRYCODE爲空注入服務,後來在構造函數填充,是這樣的:

container.Register(Component.For<IActor>().ImplementedBy<Actor>() 
     .DependsOn(Dependency.OnValue("CountryCode", null)); 

然而,這似乎凌亂和繁瑣的,特別是如果嵌套進頗深。

如果一切都失敗我也許是考慮調用一個函數之前設置的商店,但我將不得不爲每個函數如這樣做:

_serviceOne.SetCountry(CountryCode).DoSomething(); 

注:我們使用的是城堡的IOC

回答

2

這可以通過使用一個類型的工廠來實現:

public interface IServiceOneFactory 
{ 
    IServiceOne Create(CountryCode countryCode); 
} 

public class ServiceOne : IServiceOne 
{ 
    public ServiceOne(IServiceTwo servicetwo, CountryCode countryCode) 
    { 
    } 
} 

public class ActorExample 
{ 
    private raedonly IServiceOneFactory factory; 

    public ActorExample(IServiceOneFactory factory) 
    { 
     this.factory = factory; 
    } 

    public async Task ProcessAsync(Event event) 
    { 
     var serviceOne = this.factory.Create(event.CountryCode); 
    } 
} 

爲ServiceTwo一樣的...

和註冊:

kernel.AddFacility<TypedFactoryFacility>(); 
kernel.Register(Component.For<IServiceOneFactory>().AsFactory(); 
kernel.Register(Component.For<IServiceOne>().ImplementedBy<ServiceOne>()); 
kernel.Register(Component.For<IServiceTwo>().ImplementedBy<ServiceTwo>()); 
kernel.Register(Component.For<IActor>().ImplementedBy<Actor>()); 

此處瞭解詳情:

https://github.com/castleproject/Windsor/blob/master/docs/typed-factory-facility-interface-based.md

編輯: 爲了避免重複代碼,你可以創建通用工廠:

public interface IServiceFactory<T> 
{ 
    T Create(CountryCode countryCode); 
} 

public ActorExample(IServiceFactory<IServiceOne> factory) 
{ 
    this.factory = factory; 
} 

kernel.Register(Component.For(typeof(IServiceFactory<>)).AsFactory(); 
+0

感謝您的答覆,我用這個解決方案的問題是,我的服務將是加上一個工廠(1對1),這意味着每次我有一個服務,我必須建立一個工廠。我目前有9種不同的服務(我可以看到這個數字在增長)。這將會在代碼庫中增加很多工廠噪聲。 – FishFingers

+0

然後,不要在構造函數中使用CountryCode,而是通過方法調用DoSomething()傳遞它。 –

+0

是這是我最後的勝地,我希望有另一種處理這個問題的優雅方式。 – FishFingers

3

由於國家代碼是運行時數據,那麼你應該把它作爲一個pa來傳遞rameter在你的服務方法中。

如果這是不可接受的,即它沒有意義(例如從概念的角度來看)更改方法簽名以包含國家代碼,那麼您可以將國家代碼存儲在State Holder(一個只有其對象責任在於瞭解「當前」國家代碼),並讓需要知道「當前」國家代碼的對象能夠(通過依賴關係)訪問該州持有人。

這種模式進行詳細的說明與示例「控制共享狀態:國家持有模式」這篇文章部分:http://www.dotnetcurry.com/patterns-practices/1367/data-encapsulation-large-csharp-applications

+0

感謝您的回覆,保持狀態是一種可能性..但是演員將收到x包含不同國家代碼的事件。然後,我必須將其存儲在某種狀態機中,並在處理事件時以某種方式管理它的移除(這似乎非常棘手)。我擔心這個解決方案可能會打開另一堆蠕蟲。 – FishFingers

+0

@FishFingers,你能提供問題中的相關細節嗎?例如單個請求(從A到B到C ...的調用)是否在單個線程上運行? –

+0

對不起,這還不清楚爲此添加了編輯。 – FishFingers