如何使用依赖注入将状态传递到依赖关系链

时间:2017-08-01 09:01:15

标签: c# dependency-injection castle-windsor actor chain

我在依赖链中有许多服务类(服务A依赖于服务B,它取决于服务C等);它们的行为由公共参数(CountryCode)确定,该参数是在运行时定义的可能支持的国家/地区。

注意:Actors可以扩展为多个实例(不同的线程),一个事件只能由一个actor处理,下面的服务是瞬态的(尽管我可以考虑在需要时更改它)。

目前我有这样的事情:

//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注入服务为null,然后在构造函数中填充,如:

container.Register(Component.For<IActor>().ImplementedBy<Actor>()
         .DependsOn(Dependency.OnValue("CountryCode", null));
然而,这似乎很麻烦而且很麻烦,特别是如果筑巢很深的话。

如果所有其他方法都失败了,我可能会考虑在调用函数之前设置商店,但我必须为每个函数执行此操作,例如:

_serviceOne.SetCountry(CountryCode).DoSomething();

注意:我们正在使用城堡进行IOC

2 个答案:

答案 0 :(得分:3)

由于国家/地区代码是运行时数据,因此您应将其作为服务方法中的参数传递。

如果这是不可接受的,即无意义(例如从概念角度来看)更改方法签名以包含国家/地区代码,那么您可以将国家/地区代码存储在状态持有者(一个唯一负责的对象)中是知道“当前”国家代码),并且需要知道“当前”国家代码有权访问(通过依赖)此状态持有者。

本文详细解释了此模式与本文“控制共享状态:状态持有者模式”部分中的示例:http://www.dotnetcurry.com/patterns-practices/1367/data-encapsulation-large-csharp-applications

答案 1 :(得分: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();