2011-05-05 2 views
1

해당 ViewModel이있는 WPF보기가 있습니다. 모든 인스턴스는 단일 컨테이너를 통해 확인됩니다. 프리즘을 사용하기 때문에보기가 등록 된 두 개의 다른 영역에 두 개의 독립 인스턴스를 추가해야합니다. 나는 두 지역에 하나 개의 인스턴스를 추가하려고 싶다면 나는TransientLifetimeManager 삭제

InvalidOperationException이 발생합니다 : 지정된 요소가 이미 다른 요소의 논리적 아이 입니다. 먼저 의 연결을 끊으십시오.

보기가 이미 첫 번째 영역에 추가 되었기 때문에 두 번째 영역에 추가되는 경우.

이 문제는 두 인스턴스가 모두 독립 인스턴스로 채워지도록 항상 새 인스턴스를 반환하는 TransientLifetimeManager를 사용하면 쉽게 해결할 수 있습니다.

하지만 새 사용자가 로그온 할 때 하위 컨테이너를 만들기로했습니다. 모든 세션 관련 뷰 및 뷰 모델은이 하위 컨테이너를 사용하여 해결됩니다. 사용자 세션이 끝나면 모든 세션 관련 인스턴스가 삭제되도록 하위 컨테이너가 배치됩니다. 그러나 TransientLifetimeManager를 사용하면 단일 컨테이너가 해당 인스턴스를 처리 할 수 ​​없습니다.

우리는 항상 새로운 인스턴스를 반환하지만 그 인스턴스를 처리 할 수있는 평생 매니저가 필요합니다. 평생 매니저가 있습니까? 아니면 위에 설명한 내용을 달성 할 수있는 또 다른 방법이 있습니까?

+0

답변을 읽는 사람들에게 : "GCeded 될 자격이 있음"은 "즉시 또는 기술적으로 전화를 겁니다"라고 암시하지 않습니다. 엄격한 스코프/수명의 경우 이것은 큰 차이입니다. – user2864740

답변

1

기본값 인 일시적 수명 관리자를 사용하면 Unity가 만든 인스턴스에 대한 참조를 유지하지 않습니다.

따라서 인스턴스에 대한 참조가 더 이상 없을 때는 GCed됩니다.

+0

"view model first approach"와 함께 prism을 사용하는 한, 우리는 여전히 참조를 가지고 있습니다. 뷰 모델을 사용하여 뷰의 데이터 컨텍스트로 설정하는 뷰와 뷰 모델 사이에 순환 참조가 있습니다. – PVitt

+1

순환 참조는 섬의 일부인 경우 GCed해야합니다. 순환 참조에 도달 할 다른 방법이 없는지 확인하십시오. –

2

원하는 것은 싱글 톤 인스턴스를 유지하지 않고 인스턴스 컬렉션을 유지하는 ContainerControlledLifetime 관리자의 변형입니다. 불행히도이 기능은 내장 된 평생 관리자 중 하나가 아닙니다.

code for the ContainerControlledLifetimeManager을보고 매우 간단하다는 것을 알 수 있습니다. "SynchronizedGetValue"구현은 항상 null (새 인스턴스를 인스턴스화해야한다는 컨테이너에 신호)을 반환합니다. ContainerControlledLifetimeManager를 서브 클래스 화하고 해당 메소드를 재정의 할 수 있습니다.

나는 그것을 거의 썼다. 나는 당신에게 코드를 줄 수 있다고 생각합니다. :)

public class ContainerTrackedTransientLifetimeManager :  
      ContainerControlledLifetimeManager 
{ 
    protected override object SynchronizedGetValue() 
    { 
     return null; 
    } 
} 

그래야합니다. 나는 그것을 테스트하지 않았다 ... 인터페이스에서 1 대 1의 LifetimeManager to Object 관계를 위해 디자인 된 것처럼 보이지만, 그것이 더 이상 밝혀지면 SetValue를 재정의해야 할 수도있다. (컬렉션에 추가해야 할 수도있다.) 개체를 삭제하고 해당 개체의 컬렉션을 삭제합니다. 구현 방법은 다음과 같습니다.

public class ContainerTrackedTransientLifetimeManager : 
      SynchronizedLifetimeManager, IDisposable 
{ 
    private ConcurrentCollection<object> values = new ConcurrentCollection<object>(); 

    protected override object SynchronizedGetValue() 
    { 
     return null; 
    } 

    protected override void SynchronizedSetValue(object newValue) 
    { 
     values.Add(newValue); 
    } 

    public override void RemoveValue() 
    { 
     Dispose(); 
    } 

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

    protected void Dispose(bool disposing) 
    { 

     var disposables = values.OfType<IDisposable>(); 
     foreach(var disposable in disposables) 
     { 
       disposable.Dispose(); 
     } 
     values.Clear(); 
    } 

이 중 어느 것이 옳은 대답인지 잘 모르겠습니다. 그것이 당신을 위해 어떻게되는지 알려주세요.

+0

예, 어제도 시도했습니다. 귀하의 두 가지 방법뿐만 아니라 첫 번째 방법은 해결 당 새 인스턴스를 만드는 트릭을 수행하지만 컨테이너를 닫으면 해결 된 개체에 Dispose를 호출하지 않습니다. 내가 뭘 놓치고 있니? – PVitt

+0

Dammit. 방금 클래스 정의에서 IDisposable 인터페이스를 놓쳤습니다. 구현이 있지만 개체를 ​​IDisposable에 캐스팅 할 수 없습니다. 그럼에도 불구하고 HierarchicalLifetimeManager에서 ContainerTrackedTransientLifetimeManager를 파생시켜 자식 컨테이너가 삭제 될 때 인스턴스가 삭제되도록했습니다. – PVitt

+0

나는이 아이디어가 옳다고 생각하지만, Unity 자체에 패치를 적용하지 않으면 이루어질 수 없다. HierarchicalLifetimeManager에서 파생되면 내 SynchronizedGetValue 메서드가 호출되지 않습니다. ContainerControllerLifetimeManager에서 파생 된 경우 하위 컨테이너가 삭제 될 때 인스턴스가 삭제되지 않습니다. 따라서 사용자 정의 BuilderStartegy가 필요합니다. 그러나 LifetimeManager의 내부 필드 IsUsed에 대한 액세스가 필요합니다. 그래서 나는 이것이 어셈블리 밖에서도 달성 될 수 없다는 것을 두려워합니다. – PVitt