我使用SimpleInjector
作为我的IoC库。 我注册DbContext
按Web请求,它工作正常。 但我在后台线程中运行一个任务。 所以,我有一个问题创造DbContext
实例。 例如
-
Service1
有一个实例DbContext
-
Service2
有一个实例DbContext
-
Service1
和Service2
从后台线程中运行。 -
Service1
取一个实体,并把它传递给Service2
-
Service2
使用了实体,但实体是从分离DbContext
其实问题就在这里: Service1.DbContext
是从差异Service2.DbContext
。
看来,当我在ASP.NET MVC一个单独的线程运行任务, SimpleInjector
创建的新实例DbContext
每个呼叫。 尽管一些国际奥委会库(例如StructureMap
)对每个线程每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"
}
我的用户Service2
有时和AsyncService2
其他时间。
看来,当我在ASP.NET MVC一个单独的线程运行任务,SimpleInjector每个调用创建的DbContext的新实例。
该行为RegisterPerWebRequest
简单喷油器v1.5和下面的生活方式是当实例请求Web请求的上下文之外返回一个瞬态的实例(其中HttpContext.Current
为null)。 返回一个瞬态的实例在简单的喷油器设计缺陷,因为这很容易隐藏不当使用。 简单喷油器的1.6版本会抛出异常,而不是返回错误的一个瞬态的实例,沟通清楚,你有错误配置的容器。
尽管一些国际奥委会库(例如StructureMap)具有每线程每WebRequest的一个混合的生活方式,看似简单喷油器还没有一个
这是正确的,简单的喷油器没有内置在的,因为几个原因混生活方式的支持。 首先,它是一个很奇特的功能,没有多少人需要。 其次,你可以任意两种或三种生活方式混合在一起,所以这将是混合动力车的几乎无限的组合。 而在去年,它是(很)容易做这个注册自己。
虽然你可以混合每个Web请求与每个线程的生活方式,它可能会更好,当你混合每个Web请求与每终身范围 ,因为与终身范围你明确地开始和结束的范围(可以处置DbContext
当范围结束)。
从简单的喷油器2和上,你可以轻松地搭配任何数量的使用共同的生活方式Lifestyle.CreateHybrid方法。 下面是一个例子:
var hybridLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
new LifetimeScopeLifestyle());
// Register as hybrid PerWebRequest / PerLifetimeScope.
container.Register<DbContext, MyDbContext>(hybridLifestyle);
还有另外一个问题,#2是进入这一问题的深一点,你可能想看一看: 简单的喷油器:多线程在ASP.NET MVC3
UPDATE
关于您的更新。 你几乎没有。 即在后台线程运行的命令需要终身范围内运行,所以你必须明确地启动它。 这里的技巧是调用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);
}
});
}
}
鼓吹较真的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);
}
}
}
在这些装饰是注册的顺序是必不可少的当然的,因为AsyncCommandHandlerDecorator
必须包裹LifetimeScopedCommandHandlerDecorator
。 这意味着LifetimeScopedCommandHandlerDecorator
注册必须首先:
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(LifetimeScopedCommandHandlerDecorator<>),
backgroundCommandCondition);
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(AsyncCommandHandlerDecorator<>),
backgroundCommandCondition);
这个老问题#1关于这个更详细的会谈。 你一定要来看看。