2014-02-10 2 views
1

서비스가 시작될 때 모든 종속성이 만들어지고 일시적인 범위에서 주입되는 Windows 서비스를 상속 받았습니다.다중 스레드 Windows 서비스에서 Ninject를 사용하여 모든 틱에서 종속성 (DbContext)의 새 인스턴스를 얻는 방법?

우리는이 서비스에 많은 문제를 겪고 있습니다. 서비스가 실행되는 전체 시간 동안 살아있는 DbContext가 있으며, 다른 인스턴스가 매번 주입됩니다.

리팩터링을 통해 각 작업 스레드가 각 틱의 지속 기간 동안 살아있는 DbContext를 주입하도록합니다.

나는 custom scope을 보았다. 그것은 단일 스레드 응용 프로그램에 대한 괜찮아 보이지만 멀티 스레드되지 않습니다. 나는 또한 InThreadScope을 고려했다. 각 스레드는 자신의 인스턴스를 제공하지만, 스레드가 관련된 한 싱글 톤이므로 틱 요구 사항을 충족시키지 못합니다.

현재 나의 생각은 named scope extension을 사용하고 모든 틱에서 새 범위를 만드는 데 사용할 수있는 범위 팩토리를 삽입하는 것입니다.

이 방법이 있습니까? 모든 제안, 팁 또는 대안을 주시면 감사하겠습니다.

때문에 우리가 명명 된 범위를 사용하여 종료 시간 제약에 UPDATE

하지만 @ BatteryBackupUnit의 솔루션으로 깨끗하지 않았다. DbContext를 필요로하는 그래프 아래쪽에 의존성이 더 있었고 범위 팩토리를 다시 주입해야했습니다. @ BatteryBackupUnit의 솔루션을 사용하여 대신 ThreadLocal 저장소에서 동일한 인스턴스를 재사용 할 수있었습니다.

답변

3

명명 된 범위 : 동일한 스레드에서하지만 범위가 만들어지기 전에 만들어진 개체 (p.Ex. 팩토리)에서 DbContext를 만드는 경우 작동하지 않습니다. 범위가 없기 때문에 실패하거나 범위가 다르기 때문에 DbContext의 다른 인스턴스를 주입합니다. 이렇게하지 않으면 명명 된 범위 또는 호출 범위와 같은 범위가 작동 할 수 있습니다. 우리가하고있는

다음 대신하십시오 DbContext가 요청되면

, 우리는 점검하십시오 이미 하나가 있는지 ThreadLocal (http://msdn.microsoft.com/de-de/library/dd642243%28v=vs.110%29.aspx). 있는 경우에, 우리는 그것을 사용합니다. 그렇지 않으면 새 항목을 만들어 ThreadLocal<DbContext>.Value에 할당합니다. 모든 작업이 완료되면 DbContext를 해제하고 ThreadLocal<DbContext>.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(); 
    } 
} 

참고 : Ninject에, xUnit.Net,

이 또한주의 유창함 어설, 당신 것을 : 내가 nuget 패키지를 사용하고 있습니다 IUnitOfWork.Start를 ToProvider<IUnitOfWorkScope>() 바인딩으로 바꿀 수 있습니다. 물론 공급자에 해당 로직을 구현해야합니다.

0

Ninject.Extensions.UnitOfWork에 구현 된 적절한 작업 단위 범위가이 문제를 해결합니다.

설정 :

_kernel.Bind<IService>().To<Service>().InUnitOfWorkScope(); 

사용법 :

using(UnitOfWorkScope.Create()){ 
    // resolves, async/await, manual TPL ops, etc  
} 
관련 문제