C# 如何在多线程Windows服务中使用Ninject在每个刻度上获取依赖项(DbContext)的新实例?
我继承了一个Windows服务,其中所有依赖项都是在服务启动时创建的,并注入到临时作用域中 我们在这个服务中遇到了很多问题,尤其是我们有一个DbContext,它在服务运行的整个过程中都存在,并且每次都注入不同的实例 我想进行重构,以便每个工作线程都能被注入自己的DbContext,它将只在每个周期内有效 我已经看过了。对于单线程应用程序来说,它看起来不错,但对于多线程应用程序来说,它就不合适了。我也考虑过。虽然这会给每个线程提供它自己的实例,但就线程而言,它们是单线程,因此它不满足每勾号的要求 我目前的想法是使用and注入一个范围工厂,我可以使用它在每个刻度上创建一个新的范围 这是路吗?如有任何建议、提示或替代方案,将不胜感激 更新C# 如何在多线程Windows服务中使用Ninject在每个刻度上获取依赖项(DbContext)的新实例?,c#,multithreading,entity-framework,ninject,C#,Multithreading,Entity Framework,Ninject,我继承了一个Windows服务,其中所有依赖项都是在服务启动时创建的,并注入到临时作用域中 我们在这个服务中遇到了很多问题,尤其是我们有一个DbContext,它在服务运行的整个过程中都存在,并且每次都注入不同的实例 我想进行重构,以便每个工作线程都能被注入自己的DbContext,它将只在每个周期内有效 我已经看过了。对于单线程应用程序来说,它看起来不错,但对于多线程应用程序来说,它就不合适了。我也考虑过。虽然这会给每个线程提供它自己的实例,但就线程而言,它们是单线程,因此它不满足每勾号的要求
由于时间限制,我们最终使用了命名的作用域,但它没有@BatteryBackupUnit的解决方案那么干净。在图的后面还有一些依赖项,它们需要DbContext,我们必须再次注入范围工厂才能获得它。使用@ BATTYBACKUPUnE的解决方案,我们可以从<代码> THeLealDistaby/Cux>存储中重用同一个实例。要么因为没有作用域而失败,要么因为存在不同的作用域而注入另一个DbContext实例。 如果您不这样做,那么像命名范围或调用范围这样的范围可以为您工作 我们正在做以下工作: 当请求DbContext时,我们检查
ThreadLocal
()是否已经有了。如果有,我们就用那个。否则,我们将创建一个新的线程并将其分配给ThreadLocal.Value
。
完成所有操作后,我们释放DbContext并重置ThreadLocal.Value
有关示例,请参见此(简化的,不完美的)代码:
public interface IUnitOfWork
{
IUnitOfWorkScope Start();
}
internal class UnitOfWork : IUnitOfWork
{
public static readonly ThreadLocal<IUnitOfWorkScope> LocalUnitOfWork = new ThreadLocal<IUnitOfWorkScope>();
private readonly IResolutionRoot resolutionRoot;
public UnitOfWork(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IUnitOfWorkScope Start()
{
if (LocalUnitOfWork.Value == null)
{
LocalUnitOfWork.Value = this.resolutionRoot.Get<IUnitOfWorkScope>();
}
return LocalUnitOfWork.Value;
}
}
public interface IUnitOfWorkScope : IDisposable
{
Guid Id { get; }
}
public class UnitOfWorkScope : IUnitOfWorkScope
{
public UnitOfWorkScope()
{
this.Id = Guid.NewGuid();
}
public Guid Id { get; private set; }
public void Dispose()
{
UnitOfWork.LocalUnitOfWork.Value = null;
}
}
public class UnitOfWorkIntegrationTest : IDisposable
{
private readonly IKernel kernel;
public UnitOfWorkIntegrationTest()
{
this.kernel = new StandardKernel();
this.kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
this.kernel.Bind<IUnitOfWorkScope>().To<UnitOfWorkScope>();
}
[Fact]
public void MustCreateNewScopeWhenOldOneWasDisposed()
{
Guid scopeId1;
using (IUnitOfWorkScope scope = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId1 = scope.Id;
}
Guid scopeId2;
using (IUnitOfWorkScope scope = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId2 = scope.Id;
}
scopeId1.Should().NotBe(scopeId2);
}
[Fact]
public void NestedScope_MustReuseSameScope()
{
Guid scopeId1;
Guid scopeId2;
using (IUnitOfWorkScope scope1 = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId1 = scope1.Id;
using (IUnitOfWorkScope scope2 = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId2 = scope2.Id;
}
}
scopeId1.Should().Be(scopeId2);
}
[Fact]
public void MultipleThreads_MustCreateNewScopePerThread()
{
var unitOfWork = this.kernel.Get<IUnitOfWork>();
Guid scopeId1;
Guid scopeId2 = Guid.Empty;
using (IUnitOfWorkScope scope1 = unitOfWork.Start())
{
scopeId1 = scope1.Id;
Task otherThread = Task.Factory.StartNew(() =>
{
using (IUnitOfWorkScope scope2 = unitOfWork.Start())
{
scopeId2 = scope2.Id;
}
},
TaskCreationOptions.LongRunning);
if (!otherThread.Wait(TimeSpan.FromSeconds(5)))
{
throw new TimeoutException();
}
}
scopeId2.Should().NotBeEmpty();
scopeId1.Should().NotBe(scopeId2);
}
public void Dispose()
{
this.kernel.Dispose();
}
}
公共接口IUnitOfWork
{
IUnitOfWorkScope开始();
}
内部类UnitOfWork:IUnitOfWork
{
public static readonly ThreadLocal LocalUnitOfWork=new ThreadLocal();
私有只读IResolutionRoot resolutionRoot;
公共工作单元(IResolutionRoot resolutionRoot)
{
this.resolutionRoot=resolutionRoot;
}
公共IUnitOfWorkScope启动()
{
if(LocalUnitOfWork.Value==null)
{
LocalUnitOfWork.Value=this.resolutionRoot.Get();
}
返回LocalUnitOfWork.Value;
}
}
公共接口IUnitOfWorkScope:IDisposable
{
Guid Id{get;}
}
公共类UnitOfWorkScope:IUnitOfWorkScope
{
公共单位工作范围()
{
this.Id=Guid.NewGuid();
}
公共Guid Id{get;private set;}
公共空间处置()
{
UnitOfWork.LocalUnitOfWork.Value=null;
}
}
公共类UnitOfWorkIntegrationTest:IDisposable
{
私有只读IKernel内核;
公共UnitOfWorkIntegrationTest()
{
this.kernel=新的标准内核();
this.kernel.Bind().To();
this.kernel.Bind().To();
}
[事实]
public void必须在未发布时创建新闻范围()
{
Guid范围ID1;
使用(IUnitOfWorkScope scope=this.kernel.Get().Start())
{
scopeId1=scope.Id;
}
Guid范围ID2;
使用(IUnitOfWorkScope scope=this.kernel.Get().Start())
{
scopeId2=scope.Id;
}
scopeId1.Should().NotBe(scopeId2);
}
[事实]
public void NestedScope_MustReuseSameScope()
{
Guid范围ID1;
Guid范围ID2;
使用(IUnitOfWorkScope scope1=this.kernel.Get().Start())
{
scopeId1=scope1.Id;
使用(IUnitOfWorkScope scope2=this.kernel.Get().Start())
{
scopeId2=scope2.Id;
}
}
scopeId1.Should().Be(scopeId2);
}
[事实]
public void MultipleThreads\u MustCreateNewScopePerThread()
{
var unitOfWork=this.kernel.Get();
Guid范围ID1;
Guid scopeId2=Guid.Empty;
使用(IUnitOfWorkScope scope1=unitOfWork.Start())
{
scopeId1=scope1.Id;
Task otherThread=Task.Factory.StartNew(()=>
{
使用(IUnitOfWorkScope scope2=unitOfWork.Start())
{
scopeId2=scope2.Id;
}
},
TaskCreationOptions.LongRunning);
如果(!otherThread.Wait(TimeSpan.FromSeconds(5)))
{
抛出新的TimeoutException();
}
}
scopeId2.Should().NotBeEmpty();
scopeId1.Should().NotBe(scopeId2);
}
公共空间处置()
{
this.kernel.Dispose();
}
}
注意:我使用的是nuget包:ninject、xUnit.Net和Fluent断言
还请注意,您可以替换IUnitOfWork。从
ToProvider()
绑定开始。当然,您需要在提供程序中实现相应的逻辑。中实现的适当工作范围单元解决了此问题
设置:
_kernel.Bind<IService>().To<Service>().InUnitOfWorkScope();
using(UnitOfWorkScope.Create()){
// resolves, async/await, manual TPL ops, etc
}