如何通过依赖注入将状态传递到依赖链中
我在依赖链中有许多服务类(服务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
这可以通过使用一个类型的工厂来实现:
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();
由于国家代码是运行时数据,那么你应该把它作为一个pa来传递rameter在你的服务方法中。
如果这是不可接受的,即它没有意义(例如从概念的角度来看)更改方法签名以包含国家代码,那么您可以将国家代码存储在State Holder(一个只有其对象责任在于了解“当前”国家代码),并让需要知道“当前”国家代码的对象能够(通过依赖关系)访问该州持有人。
这种模式进行详细的说明与示例“控制共享状态:国家持有模式”这篇文章部分:http://www.dotnetcurry.com/patterns-practices/1367/data-encapsulation-large-csharp-applications
感谢您的回复,保持状态是一种可能性..但是演员将收到x包含不同国家代码的事件。然后,我必须将其存储在某种状态机中,并在处理事件时以某种方式管理它的移除(这似乎非常棘手)。我担心这个解决方案可能会打开另一堆蠕虫。 – FishFingers
@FishFingers,你能提供问题中的相关细节吗?例如单个请求(从A到B到C ...的调用)是否在单个线程上运行? –
对不起,这还不清楚为此添加了编辑。 – FishFingers
感谢您的答复,我用这个解决方案的问题是,我的服务将是加上一个工厂(1对1),这意味着每次我有一个服务,我必须建立一个工厂。我目前有9种不同的服务(我可以看到这个数字在增长)。这将会在代码库中增加很多工厂噪声。 – FishFingers
然后,不要在构造函数中使用CountryCode,而是通过方法调用DoSomething()传递它。 –
是这是我最后的胜地,我希望有另一种处理这个问题的优雅方式。 – FishFingers