2017-08-01 82 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