在多线程应用程序中正确使用 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();
}
}
我有几个问题:
- 代码不干。我不想为我的每个服务编写 ThreadSafeService-Decorator。而且我不想为我的每项服务都设置一个新的 ServiceFactory。
- 我的 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 来创建实际服务
我正在使用与 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();
}
}
我有几个问题:
- 代码不干。我不想为我的每个服务编写 ThreadSafeService-Decorator。而且我不想为我的每项服务都设置一个新的 ServiceFactory。
- 我的 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 来创建实际服务