2017-09-23 7 views
0

아래에 스택 오버플로 예외를 생성하는 완료된 C# 응용 프로그램이 있습니다. 소스 코드를 검토하면 왜 스택 오버플로 예외가 발생하는지 알 수 있으므로 실제로 발생하는 이유를 진단하지 않습니다. 처리하는 가장 좋은 방법은 무엇인지 알고 싶습니다.Unity Container에서 처분을 호출하는 가장 좋은 방법은 무엇입니까?

1) 통일에 대한 모든 언급은 레지스트리라는 클래스 내에 캡슐화되어 있으므로 쉽게 업그레이드 할 수 있습니다. 나는 유니티 컨테이너가 가능한 다른 곳에서 쓰레기를 버리는 것을 원하지 않는다. 이론적으로, 나는 그것이 나올 때는 5로 업그레이드 할 수 있어야한다. 또는 과감한 처분 변경이 필요하다면 다른 DI 프레임 워크 나 다른 것으로 교체 할 수 있어야한다.

2) 컨테이너가 제어하는 ​​클래스의 생성자에서 사용할 수 있도록 레지스트리가 단일 컨테이너로 제어되기를 원합니다. (예 : FirstSingleInstance)

3) 단일 용기를 처리하는 것이 좋습니다. 3) IRegistry와 Registry 모두 IDisposable에서 상속합니다.

4) Registry는 자신의 생성자에 Unity Container를 생성하므로 registry.dispose가 호출 될 때 단일 컨테이너를 처리해야한다고 가정합니다.

5) 레지스트리에서 제어하는 ​​다른 모든 클래스는 단일 인스턴스 클래스로 간주되므로 ContainerControlledLifetimeManager로 등록합니다. 컨테이너가 처분 될 때 해당 인스턴스가 폐기 될 것으로 예상됩니다.

이 상황을 처리하는 가장 좋은 방법은 무엇입니까?

) 레지스트리에서 dispose를 호출하지 마십시오. 프로세스 스레드의 수명 동안 살게 하시겠습니까?

b) 레지스트리를 유니티 컨테이너에 의해 제어되도록 확장하지 마십시오. 레지스트리에서 dispose를 호출해도 stackoverflow 예외는 발생하지 않습니다. FirstSingleInstance 클래스를 구성하는 방법은 내가 검토해야 할 부분입니다.

d) 기타?

여기
using System; 
using Microsoft.Practices.Unity; 

namespace DIProblem.Console 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IRegistry registry = CreateRegistry(); 
      IFirstSingleInstance theInstance = registry.Resolve<IFirstSingleInstance>(); 
      theInstance.DoThis(); 
      registry.Dispose(); // stack overflow here because of infinite dispose loop 
     } 

     static IRegistry CreateRegistry() => new Registry(); 
    } 

    public class FirstSingleInstance : IFirstSingleInstance 
    { 
     private IRegistry _registry; 

     public FirstSingleInstance(IRegistry reg) 
     { 
      _registry = reg; 
     } 

     public void DoThis() 
     { 

      System.Console.WriteLine("This Was Done."); 

      _registry.Resolve<ISecondSingleInstance>().DoThisToo(); 

     } 
    } 

    public class SecondSingleInstance : ISecondSingleInstance 
    { 
     private IRegistry _registry; 

     public SecondSingleInstance(IRegistry reg) 
     { 
      _registry = reg; 
     } 

     public void DoThisToo() 
     { 
      System.Console.WriteLine("This Was Done too."); 
     } 
    } 

    public interface ISecondSingleInstance 
    { 
     void DoThisToo(); 
    } 


    public interface IFirstSingleInstance 
    { 
     void DoThis(); 
    } 


    public class Registry : IRegistry, IDisposable 
    { 
     public Registry() 
     { 
      _container = new UnityContainer(); 

      RegisterInstance<IFirstSingleInstance, FirstSingleInstance>(); 
      RegisterInstance<ISecondSingleInstance, SecondSingleInstance>(); 

      _container.RegisterInstance<IRegistry>(this); 
     } 

     private UnityContainer _container; 

     public void RegisterInstance<T1, T2>() where T2 : class, T1 => _container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager()); 

     public T Resolve<T>() => _container.Resolve<T>(); 

     public void Dispose() 
     { 
      Dispose(true); 
      System.GC.SuppressFinalize(this); 
     } 

     protected virtual void Dispose(bool disposing) 
     { 
      _container?.Dispose(); 
      _container = null; 
     } 
    } 

    public interface IRegistry : IDisposable 
    { 
     T Resolve<T>(); 
     void RegisterInstance<T1, T2>() where T2 : class, T1; 
    } 
} 

이 방법은 합리적인 것 같습니다 어떤에서 도와 주셔서 감사 관련된 모든 조각을 가지고 내가 쓴 응용 프로그램입니다.

+0

왜 정확하게 'IRegistry'를 컨테이너에 등록 하시겠습니까? – Steven

+0

FirstSingleInstance 클래스는 그것을 public 생성자의 매개 변수로 받아들입니다. – Jason

+0

왜 의존성이 필요한가? 기본적으로 Composition Root 인 컨테이너가 아니라 레지스트리가 필요하다고 상상할 수 있습니다. – Steven

답변

1

다음 코드는 Service Locator anti-pattern을 사용하지 않고 대신 생성자 삽입에만 의존하여 제어 반전을 적용합니다. 그 결과 스택 오버 플로우 예외를 발생시키지 않는보다 간단하고 유지 보수가 용이하며 테스트가 가능한 응용 프로그램이 만들어집니다.

class Program 
{ 
    static void Main(string[] args) 
    { 
     using (var container = Registry.BuildContainer()) 
     { 
      var theInstance = registry.Resolve<IFirstSingleInstance>(); 
      theInstance.DoThis(); 
     } 
    } 
} 

public static class Registry 
{ 
    public static UnityContainer BuildContainer() 
    { 
     var container = new UnityContainer(); 

     container.RegisterType<IFirstSingleInstance, FirstSingleInstance>(Singleton); 
     container.RegisterType<ISecondSingleInstance, SecondSingleInstance>(Singleton); 

     return container; 
    } 

    private static ContainerControlledLifetimeManager Singleton => 
     new ContainerControlledLifetimeManager(); 
} 

public interface ISecondSingleInstance 
{ 
    void DoThisToo(); 
} 

public interface IFirstSingleInstance 
{ 
    void DoThis(); 
} 

public class FirstSingleInstance : IFirstSingleInstance 
{ 
    private ISecondSingleInstance _second; 

    public FirstSingleInstance(ISecondSingleInstance second) 
    { 
     _second = second; 
    } 

    public void DoThis() 
    { 
     System.Console.WriteLine("This Was Done."); 
     _second.DoThisToo(); 
    } 
} 

public class SecondSingleInstance : ISecondSingleInstance 
{ 
    public SecondSingleInstance(/* other dependencies here */) 
    { 
    } 

    public void DoThisToo() 
    { 
     System.Console.WriteLine("This Was Done too."); 
    } 
} 
+1

놀랍도록 간단합니다. 스티븐 감사합니다! – Jason

+0

btw, 방금 DI 컨테이너를 사용하기 시작한 것 같습니다. 이 경우 Unity는 다른 DI 컨테이너와 비교하여 기능면에서 1 년이 넘었고 2) Microsoft [중지됨] (https://blogs.msdn.microsoft.com/dotnet/2015)에 비해 최상의 선택이 아닐 수도 있습니다./08/21/the-future-of-unity /)가 2015 년 이후로 유지되고 있으며, 최신 출시 (https://github.com/unitycontainer/unity/releases)는 2014 년 5 월부터입니다. – Steven

+0

그리고 DI에 관하여 [좋은 책] (https://www.manning.com/seemann2/)을 읽으려면 ;-) – Steven