2017-12-28 34 views
0

DisposableDbObject (IDisposable 구현) 타사 개체를 사용하는 MyService 클래스가 포함 된 라이브러리를 만듭니다. Autofac ContainerBuilder 확장을 하나의 인스턴스로 등록합니다 (객체 생성에는 비용이 많이 듭니다). 문제는 한 번에 DisposableDbObject 인스턴스를 새로 고쳐야한다는 것입니다 (파일에서 새 버전의 데이터베이스를로드해야하는 일부 메모리 내장 DB의 래퍼입니다). 내가 아는 한, SingletonInstance (그리고 ContainerBuilder.Update은 더 이상 사용되지 않음) 구성 요소의 참조를 대체하는 안전한 방법이 없으므로 DisposableDbObjectDisposableDbObjectProvider 클래스로 감싸서 싱글 톤으로 등록하고 자유롭게 손을 대면서 밑에있다. 그래서 제 설정은 이렇게됩니다.Autofac에서 싱글 톤으로 등록 된 일회용 개체 교체

// DisposableDbObjectProvider.cs 
public interface IDisposableDbObjectProvider 
{ 
    DisposableDbObject GetDb(); 
} 

public class DisposableDbObjectProvider : IDisposableDbObjectProvider 
{ 
    private DisposableDbObject _obj; 
    public DisposableDbObjectProvider() 
    { 
     _obj = new DisposableDbObject("D:\\path\to\file"); 
    } 
    public DisposableDbObject GetDb() 
    { 
     return _obj; 
    } 
    public void UpdateDb() 
    { 
     _obj = new DisposableDbObject("D:\\path\to\new\file"); 
    } 
} 

// MyService.cs 
interface IMyService 
{ 
    string GetStuffFromDb(); 
} 

class MyService 
{ 
    private DisposableDbObjectProvider _provider; 

    class MyService(IDisposableDbObjectProvider provider) 
    { 
     _provider = provider; 
    } 

    public string GetStuffFromDb() 
    { 
     return _provider.GetDb().Read(...); 
    } 
} 


// AutofacExtensions.cs 
static class AutofacExtensions 
{ 
    public static ContainerBuilder WithMyService(this ContainerBuilder builder) 
    { 
      builder.RegisterType<DisposableDbObjectProvider >().As<IDisposableDbObjectProvider>().SingleInstance(); 
      builder.RegisterType<MyService>().As<IMyService>(); 
    } 
} 

이제는이 설정에 3 가지 문제가 있습니다.

  1. (ASP.NET WebApi2 같은) 다중 스레드 클라이언트 앱 MyService 하나 하나 개 개의 스레드 레지스터 (BE 그것을 ASP.NET 의뢰처) 스레드는 상태 업데이트가 수행 된 경우에 개체의 두 가지 버전을 액세스 할 실행 (내 구체적인 경우에 이것은 충분히 좋을지도 모릅니다. 그러나이를 피하고 싶습니다.)

  2. DisposableDbObject 참조를 바꾸면 이전에는 Dispose이 필요합니다. 이제 해당 개체에 대한 참조를 유지하는 N> = 1 스레드가있을 수 있으며 DisposeDisposableDbObjectProvider에 호출하는 동안 해당 스레드는 ObjectDisposedException으로 끝날 수 있습니다.

  3. 클라이언트가 사용하는 개체를 폐기해야하는 규칙을 위반합니다. 나는 생각했다

한 가지 방법은 IsAlive를 통해 (static 필드로하고 WeakReference 추적 목록으로 이전 참조 저장 각 업데이트에 DisposableDbObject으로 과도 수집 쓰레기입니다 참조를 위해 그것을 스캔 DisposableDbObjectProvider의 등록을 변경하는 것입니다 DisposableDbObjectProvider.Dispose가 beha 않을 경우 속성) 아래

public class DisposableDbObjectProvider : IDisposableDbObjectProvider, IDisposable 
{ 
    private static DisposableDbObject _obj = new DisposableDbObject("D:\\path\to\file"); 
    private static List<WeakReference> _oldRefs; 
    public DisposableDbObject GetDb() 
    { 
     return _obj; 
    } 
    public void UpdateDb() 
    { 
     _oldRefs.Add(_obj); 
     _obj = new DisposableDbObject("D:\\path\to\new\file"); 
    } 

    public void Dispose() 
    { 
     var deadRefs = _oldRefs.Where(x => !x.IsAlive); 
     oldRefs = oldRefs.Exclude(deadRefs); 
     foreach(var deadRef in deadRefs) 
     { 
      ((IDisposable) deadRef.Target).Dispose(); 
     } 
    } 
} 

처럼, 이들에 Dispose를 호출하지만 여전히 나는이 soultion에 대해 매우 안심하지 않습니다 더이 그 바로 해결할 수있는 문제는 (말할 수 없다 여러 스레드가 동시에 호출하는 동안

이러한 문제를 극복하는 가장 좋은 방법은 무엇입니까? 물론 싱글 톤 등록 문제를 우회하는 나의 해결책에 결함이있을 수 있습니다. 더 좋은 접근법이 있다면 그것에 대해 듣고 싶습니다.

+0

DisposableDbObject를 새로 고치는시기는 프로그램에서 어떻게 결정합니까? 패턴이 있으면 싱글 톤으로 등록을 중지하고 올바른 수의 인스턴스를 전달할 수 있습니다. –

+0

'DisposableDbObject'의 소스 코드를 보여주십시오. _ 그렇지 않으면 명확하지 않은 기회가 생길 수도 있습니다 ._ – mjwills

답변

1

우선 스레드 안전이 누락되었다고 말할 수 있습니다. 객체가 사용 중이면 다른 모든 스레드가 작업을 완료 할 때까지 객체를 업데이트 할 수 없습니다. 업데이트 루틴과 동일합니다. 완료되기 전에 다른 스레드가 객체 나 메소드에 액세스 할 수 없어야합니다. 그것은 사소한 것이 아니며 깊은 이해없이 그러한 것을 구현하려고하지 않을 것입니다. 좋은 소식은 복수 스레드가 읽을 수 있도록 설계된 ReaderWriterLockSlim class이 있지만 한 번에 하나의 스레드 만 쓸 수 있다는 것입니다. 또한 기존의 객체를 읽기 잠금을 얻지 않은 동안 쓰기로 처 리할 수 있으며 반대의 경우도 마찬가지입니다.

public class DisposableDbObjectProvider : IDisposableDbObjectProvider, IDisposable 
{ 
    private DisposableDbObject _obj = new DisposableDbObject("D:\\path\to\file"); 
    private ReaderWriteLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); 

    public DisposableDbObject AquireDb() 
    { 
     if(_lock.TryEnterReaderLock(100)) // how long to wait until entering fails 
     { 
      return _obj; 
     } 
     else 
     { 
      // unable to enter read lock in timeout 
      // do something 
     } 
    } 

    public void ReleaseDb() 
    { 
     // we need to exit lock after we are done with reading 
     _lock.ExitReadLock(); 
    } 

    public void UpdateDb() 
    { 
     if(_lock.TryEnterWriteLock(500)) // how long to wait until entering fails 
     { 
      _obj.Dispose(); 
      _obj = new DisposableDbObject("D:\\path\to\new\file"); 
      _lock.ExitWriteLock(); // We need to leave write lock to let read lock to be acquired 
     } 
     else 
     { 
      // unable to enter write lock in timeout 
      // do something 
     } 
    } 

    public void Dispose() 
    { 
     _obj.Dispose(); 
    } 
} 

그것은 당신을 위해 작동 할 수 있지만 아마 당신이 길을 따라 약간의 비틀기를 수행해야하지만, 아이디어가 희망 명확하고 도움이됩니다.