在多线程应用程序中正确使用 IOC(装饰器或拦截器?)

Correct use of IOC in a multi-threaded application (Decorator or Interceptor?)

我正在使用与 this post 中的方法非常相似的方法进行项目。我正在使用 Unity 作为 DI 框架,我对此很陌生。 有一个 JobController 应该在新任务中启动注入服务 n 次。该代码看起来与 Chads initial post.

中的代码几乎相同
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. 代码不干。我不想为我的每个服务编写 ThreadSafeService-Decorator。而且我不想为我的每项服务都设置一个新的 ServiceFactory。
  2. 我的 ServiceFactories 的实现必须知道如何创建具体服务,但由于这些服务作为内部 类 存储在另一个程序集中,并且还具有其他依赖项(对存储库等)必须被注入,我不确定在哪里放置工厂以及如何使用 Unity 创建具有所有依赖项的具体服务。

看完 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: 是和不是。这是一个真实世界的项目,但我仍在起草架构。为此,我想要一个小原型并根据要求检查它。我确实有几种服务可以通过不同的 UIs 使用。 在这种情况下,UI 是一个处理传入作业的控制台应用程序。一项工作必须处理多个单元。为此,每个 Job 都使用一个具体的服务实现,但因为服务被设计为仅处理一个单元。当然,我可以在我的 JobController 中做类似的事情。

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

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

我会做这样的事情

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 设置为仅注入工厂方法(来自您的服务工厂 class)。该工厂可以封装 Unity 来创建实际服务