如何使用Ninject在多线程Windows服务在每个替得到的依赖(的DbContext)的新实例? [英] How to use Ninject in a multi-threaded Windows service to get new instances of a dependency (DbContext) on every tick?

查看:284
本文介绍了如何使用Ninject在多线程Windows服务在每个替得到的依赖(的DbContext)的新实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我继承,所有的依赖关系创建的服务启动时,并在短暂的范围内被注入一个Wi​​ndows服务。

I have inherited a Windows service where all the dependencies are created when the service starts and are injected in the transient scope.

我们遇到了一些问题,这项服务,并非最不重要的,我们有一个的DbContext其生活的服务正在运行的全部时间,和它不同的实例每次注射。

We are having a number of problems with this service, not least we have a DbContext which lives for the whole time the service is running, and different instances of it are injected each time.

我想重构。让每个工作者线程得到它自己的DbContext注入,将生活的每个刻度的只是时间

I would like to refactor so that each worker thread gets it’s own DbContext injected which will live for just the duration of each tick.

我已经看过了的自定义范围。它看起来罚款单线程应用程序,而不是多线程。我也算是 InThreadScope 。虽然,将给予每个线程它自己的实例,他们是单身至于线程而言,因此它不会每个时钟周期的要求履行。

I have looked at the custom scope. It looks fine for a single threaded app, but not multi-threaded. I also considered InThreadScope. Whilst that would give each thread it’s own instance, they are singletons as far as the thread is concerned so it does not fulfil the per tick requirement.

我目前的想法是使用命名范围扩展并注入一个范围的工厂,我可以用它来创建一个新的范围在每打勾。

My current thinking is to use the named scope extension and to inject a scope factory which I can use to create a new scope on every tick.

这是要走的路?任何建议,提示或替代方案,将不胜感激。

Is this the way to go? Any suggestions, tips or alternatives would be appreciated.

更新

由于时间限制,我们结束了使用指定的范围,但它并不像@ BatteryBackupUnit的解决方案一样干净。有一些相关性进一步下降,这需要的DbContext图中,我们不得不再次注入范围工厂得到它。使用@ BatteryBackupUnit的解决方案,我们可以重用从的ThreadLocal 存储,而不是同一个实例。

Due to a time constraint we ended up using the named scope, but it wasn't as clean as @BatteryBackupUnit's solution. There were some dependencies further down the graph which needed a DbContext and we had to inject the scope factory again to get it. Using @BatteryBackupUnit's solution we could have reused the same instance from the ThreadLocal storage instead.

推荐答案

关于命名范围:想想看,当你在同一个线程,但是从一个对象(p.Ex.厂)创建范围之前,这是创建创建的DbContext,它不会工作。它要么会失败,因为没有范围,或者因为有不同的范围,它会注入的DbContext的另一个实例。
如果你不这样做,然后像一个范围命名范围或致电范围可以为你工作。

Regarding Named Scope: Consider that when you are creating a DbContext from the same thread but from an object (p.Ex. factory) which was created before the scope was created, it won't work. Either it will fail because there is no scope, or it will inject another instance of DbContext because there is a different scope. If you don't do this, then a scope like named scope or call scope can work for you.

我们正在做的,而不是如下:

We are doing the following instead:

当被请求的DbContext,我们检查的ThreadLocal
http://msdn.microsoft.com/de-de/library/dd642243%28v= vs.110%29.aspx )是否已经之一。如果有,我们使用的那一个。否则,我们创建了一个新的,并把它分配给的ThreadLocal<&的DbContext GT; .value的
一旦所有操作都完成后,我们发布的DbContext和重置的ThreadLocal<&的DbContext GT; .value的

When a DbContext is requested, we check a ThreadLocal (http://msdn.microsoft.com/de-de/library/dd642243%28v=vs.110%29.aspx) whether there is already one. In case there is, we use that one. Otherwise, we create a new one and assign it to the ThreadLocal<DbContext>.Value. Once all operations are done, we release the DbContext and reset the ThreadLocal<DbContext>.Value.

为例看到这个(简化,不完美)代码:

See this (simplified, not perfect) code for an example:

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();
    }
}

请注意:我使用的NuGet包:ninject, xUnit.Net,流利的断言

Note: i'm using nuget packages: ninject, xUnit.Net, Fluent Assertions

另外请注意,您可以用 ToProvider<更换IUnitOfWork.Start; IUnitOfWorkScope>()绑定。当然,你需要实现在提供相应的逻辑。

Also note, that you can replace the IUnitOfWork.Start with a ToProvider<IUnitOfWorkScope>() binding. Of course you need to implement the corresponding logic in the provider.

这篇关于如何使用Ninject在多线程Windows服务在每个替得到的依赖(的DbContext)的新实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆