在多线程应用程序(Decorator或Interceptor?)中正确使用IOC

时间:2015-02-06 12:39:22

标签: c# multithreading dependency-injection ioc-container

我正在使用与this post中类似的方法开展项目。我使用Unity作为DI-Framework,我对此非常新。 有一个JobController应该在一个新的Task中启动一个注入的服务n次。代码与Chads的初始帖子看起来非常相似。

public class Controller
{
   IService _service;

   public Controller(IService service)
   {
       this._service = service;
   }

   public Action DoSomethingManyTimes()
   {
      for(int i =0; i < numberOfTimes; i++)
      {
         Task.Factory.StartNew(() =>
         {  
            _service.DoSomething();
         });
      }
   }
}

由于我的具体服务不是线程安全的,我遵循Marks方法并实现了一个装饰器,每个具体服务需要一个工厂,如Marks答案中的例子。我的课程看起来像这样:

public ThreadSafeService1 : IService
{
   private readonly IServiceFactory factory;

   public ThreadSafeService1(IServiceFactory factory)
   {
       this.factory = factory;
   }

   public void DoSomething()
   {
       this.factory.Create().DoSomething();
   }
}

internal class Service1Factory : IServiceFactory
{
    public IService Create()
    {
        return new Service1();
    }
}

我有几个问题:

  1. 代码不是DRY。我不想为我的每个服务编写ThreadSafeService-Decorator代码。而且我也不想为我的每项服务都有一个新的ServiceFactory。
  2. 我的ServiceFactories的实现必须知道如何创建具体的服务,但是因为这些服务作为内部类存储在另一个程序集中,并且还有其他必须注入的依赖项(对于存储库等),我不是确定在哪里放置工厂以及如何使用Unity创建具有所有依赖项的具体服务。
  3. 在观看Marks video后,我试图通过实现Unity的拦截器来解决第一个问题,如下所示:

    internal class ThreadSafeServiceInterceptor : IInterceptionBehavior
    {
        IServiceFactory serviceFactory;
    
        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {           
            serviceFactory.Create().Start();
            return getNext()(input, getNext);
        }
    
        ...
    }
    

    serviceFactory是通过构造函数注入的。除了我还没有为问题2找到一个好的解决方案之外,我怎么能告诉Unity哪个具体的SessionFactory必须注入我的拦截器?这取决于启动的服务。在我的申请中,我做了类似的事情:

    var service = container.Resolve<IService>("Service1");
    var controller = container.Resolve<JobController>(new ParameterOverride("service", service));
    
     controller.StartWork();
    

    但当然服务无法解决。 Unity无法注入serviceFactory,因为IServiceFactory有多个注册(对于我的每个服务)。 除此之外,我的拦截器将创建一个新的服务实例并启动它。之后,当调用getNext()()时,已解析的服务也会启动并执行相同的操作。但由于可能有其他拦截器,我必须调用getNext()。

    所有这些让我觉得我不是正确的方式。据我了解,拦截器应该处理跨领域的问题。但是,提供多线程服务并不是一个贯穿各领域的关注,对吧?

    也许有人可以指出我正确的方向?

    更新

    @oleksii: 是的,不是。这是一个真实世界的项目,但我仍在起草架构。为此,我想要一个小的原型并根据要求进行检查。我有几个服务可以通过不同的用户界面消费。 在这种情况下,UI是一个处理传入作业的控制台应用程序。一项工作必须处理几个单位。为此,每个Job都使用一个具体的服务实现,但因为服务只是为了处理一个单元。当然我可以在JobController中做类似的事情。

    foreach(var unit in unitsToProcess)
    {
       _service.DoSomethingWithUnit(unit);
    }
    

    但我想并行处理几个单位。这就是为什么我认为我需要几个具体服务的实例。

1 个答案:

答案 0 :(得分:3)

我会做这样的事情

public class Controller
{
    Func<IService> _factory;

public Controller(Func<IService> factory)
{
   _factory = factory;
}

public Action DoSomethingManyTimes()
{
  for(int i =0; i < numberOfTimes; i++)
  {
     Task.Factory.StartNew(() =>
     {  
        _factory().DoSomething();
     });
  }
}
}

任何体面的DI容器都应该知道如何解析Func(我不使用Unity,默认情况下autofac会这样做)。因此,您为服务注入工厂,然后每个任务都有自己的服务实例。

我通常将这种方法用于单例存储库,其中我注入了DbConnection的工厂或我需要的任何东西,然后每个方法将使用不同的实例并且不共享任何状态。

更新

服务工厂可以是抽象工厂,因此每个服务没有一个工厂,但是所有服务都有一个工厂。也许这样的事情

Func<string,IService>

你设置Unity只注入工厂方法(来自你的服务工厂类)。该工厂可以封装Unity以创建实际服务