jav*_*iry 14 c# dependency-injection inversion-of-control simple-injector
我SimpleInjector用作我的IoC库.我DbContext根据网络请求注册,它工作正常.但是我有一个任务是在后台线程中运行它.所以,我有一个问题来创建DbContext实例.例如
Service1 有一个实例 DbContextService2 有一个实例 DbContext Service1并Service2从后台线程运行.Service1 获取实体并将其传递给 Service2Service2 使用该实体,但实体与之分离 DbContext实际上问题出在这里:Service1.DbContext与众不同Service2.DbContext.
当我在ASP.NET MVC中的一个单独的线程中运行任务时,似乎为每个调用SimpleInjector创建一个新实例DbContext.虽然一些IoC库(例如StructureMap)对于每个web-per-webrequest具有混合生活方式,但似乎SimpleInjector没有一个.我对吗?
你有什么想法解决这个问题SimpleInjector吗?提前致谢.
编辑:
我的服务在这里:
class Service1 : IService1 {
public Service1(MyDbContext context) { }
}
class Service2 : IService2 {
public Service2(MyDbContext context, IService1 service1) { }
}
class SyncServiceUsage {
public SyncServiceUsage(Service2 service2) {
// use Service2 (and Service1 and DbContext) from HttpContext.Current
}
}
class AsyncServiceUsage {
public AsyncServiceUsage(Service2 service2) {
// use Service2 (and Service1 and DbContext) from background thread
}
}
public class AsyncCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand> where TCommand : ICommand {
private readonly Func<ICommandHandler<TCommand>> _factory;
public AsyncCommandHandlerDecorator(Func<ICommandHandler<TCommand>> factory) {
_factory = factory;
}
public void Handle(TCommand command) {
ThreadPool.QueueUserWorkItem(_ => {
// Create new handler in this thread.
var handler = _factory();
handler.Handle(command);
});
}
}
void InitializeSimpleInjector() {
register AsyncCommandHandlerDecorator for services (commands actually) that starts with "Async"
}
Run Code Online (Sandbox Code Playgroud)
我Service2有时和AsyncService2其他时间用户.
Ste*_*ven 17
看来,当我在ASP.NET MVC的一个单独的线程中运行一个任务时,SimpleInjector为每个调用创建一个新的DbContext实例.
RegisterPerWebRequestSimple Injector v1.5及更低版本的生活方式的行为是在Web请求的上下文之外请求实例时返回瞬态实例(其中HttpContext.Current为null).返回瞬态实例是Simple Injector中的一个设计缺陷,因为这样可以很容易地隐藏不正确的用法.Simple Injector的1.6版将抛出异常,而不是错误地返回瞬态实例,以清楚地表明您错误配置了容器.
虽然一些IoC库(例如StructureMap)对于每个web-per-webrequest具有混合生活方式,但似乎Simple Injector没有一个
由于几个原因,Simple Injector没有内置的混合生活方式支持是正确的.首先,它是一个非常奇特的功能,没有多少人需要.其次,你可以将任何两种或三种生活方式混合在一起,这样几乎是混合动力的无穷无尽的组合.最后,(非常)容易自己注册.
虽然您可以将Per Web Request与Per Thread生活方式混合使用,但是当您将Per Web Request与Per Lifetime Scope混合使用时可能会更好,因为使用Lifetime Scope可以明确地开始和完成范围(并且可以DbContext在范围结束时处置) .
从Simple Injector 2开始,您可以使用Lifestyle.CreateHybrid方法轻松混合任意数量的生活方式.这是一个例子:
var hybridLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
new LifetimeScopeLifestyle());
// Register as hybrid PerWebRequest / PerLifetimeScope.
container.Register<DbContext, MyDbContext>(hybridLifestyle);
Run Code Online (Sandbox Code Playgroud)
还有另一个Stackoverflow问题深入讨论这个主题,你可能想看看:Simple Injector:MVC3 ASP.NET中的多线程
UPDATE
关于您的更新.你快到了.在后台线程上运行的命令需要在Lifetime Scope中运行,因此您必须明确地启动它.这里的技巧是调用BeginLifetimeScope新线程,但在创建实际命令处理程序(及其依赖项)之前.换句话说,最好的方法是在装饰器内部.
最简单的解决方案是更新您AsyncCommandHandlerDecorator的添加范围:
public class AsyncCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly Container _container;
private readonly Func<ICommandHandler<TCommand>> _factory;
public AsyncCommandHandlerDecorator(Container container,
Func<ICommandHandler<TCommand>> factory)
{
_container = container;
_factory = factory;
}
public void Handle(TCommand command)
{
ThreadPool.QueueUserWorkItem(_ =>
{
using (_container.BeginLifetimeScope())
{
// Create new handler in this thread
// and inside the lifetime scope.
var handler = _factory();
handler.Handle(command);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
倡导SOLID原则的纯粹主义者会大声说这个类违反了单一责任原则,因为这个装饰器都在新线程上运行命令并启动新的生命周期范围.我不会担心这一点,因为我认为启动后台线程和启动生命周期范围之间存在密切关系(不管怎样,你都不会使用其中一个).但是,您仍然可以轻松地AsyncCommandHandlerDecorator保持原状并创建一个新的LifetimeScopedCommandHandlerDecorator,如下所示:
public class LifetimeScopedCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly Container _container;
private readonly Func<ICommandHandler<TCommand>> _factory;
public LifetimeScopedCommandHandlerDecorator(Container container,
Func<ICommandHandler<TCommand>> factory)
{
_container = container;
_factory = factory;
}
public void Handle(TCommand command)
{
using (_container.BeginLifetimeScope())
{
// The handler must be created inside the lifetime scope.
var handler = _factory();
handler.Handle(command);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这些装饰器的注册顺序当然是必不可少的,因为AsyncCommandHandlerDecorator 必须包装LifetimeScopedCommandHandlerDecorator.这意味着LifetimeScopedCommandHandlerDecorator必须首先注册:
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(LifetimeScopedCommandHandlerDecorator<>),
backgroundCommandCondition);
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(AsyncCommandHandlerDecorator<>),
backgroundCommandCondition);
Run Code Online (Sandbox Code Playgroud)
这个旧的Stackoverflow问题更详细地讨论了这个问题.你一定要看看.
| 归档时间: |
|
| 查看次数: |
5780 次 |
| 最近记录: |