2011-05-05 83 views
1

我有一个WPF视图有一个相应的ViewModel。所有实例都通过统一容器解决。因为我使用棱镜,所以我需要两个独立的视图实例将其添加到视图注册到的两个不同区域。如果我想尝试一个实例添加到这两个区域,我得到一个有没有配置TransientLifetimeManager

InvalidOperationException异常:指定 元素已经是另一个元素的逻辑子 。首先断开它 。

当视图被添加到第二个区域时,因为它已被添加到第一个区域。

这个问题很容易通过使用一个总是返回一个新实例的TransientLifetimeManager来解决,因此这两个区域都会被一个独立实例填充。

但是我们决定在新用户登录时创建一个子容器。每个会话相关的视图和视图模型都使用此子容器进行解析。当用户的会话结束时,处理子容器,以便还处置每个与会话有关的实例。但是使用TransientLifetimeManager时,统一容器不能处理这些实例。

我们需要的是一个总是返回一个新实例但也能够处理这些实例的终生管理器。有没有这样的人生经理?还是有另一种方法来实现我上面描述的?

+0

对于那些阅读答案的人:“使有资格成为GC'ed”并不意味着要求[立即或技术上的]处置“。对于严格的范围/寿命来说,这是一个很大的差异。 – user2864740 2015-03-03 21:47:56

回答

1

当您使用瞬态生存管理器(这是默认值)时,Unity不保留对创建实例的引用。

因此,当没有更多的实例引用时,它将被GCed。

+0

就我们所用的“视图模型第一方法”而言,我们仍然有参考文献。视图和视图模型之间有一个循环引用,它使用视图模型将自己设置为视图的数据上下文。 – PVitt 2011-05-05 12:32:01

+1

循环引用如果它们是岛的一部分,也应该被GCed。确保没有其他方法可以达到循环参考 – 2011-05-05 17:02:42

2

你想要的声音听起来像是一个ContainerControlledLifetime管理器的变体,它不维护单例实例,而是一组实例。不幸的是,这不是内置的终生管理者之一。

你可以看看code for the ContainerControlledLifetimeManager,看看它很简单。您的“SynchronizedGetValue”实现将始终返回null(向容器发信号通知需要实例化新实例)。您可以继承ContainerControlledLifetimeManager并覆盖该方法。

我写了很多东西。我想我可以给你的代码。 :)

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

这应该工作。我没有测试它...从界面看来,它看起来像是为1到1的LifetimeManager到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 2011-05-06 07:43:36

+0

该死的。我只是错过了类定义中的IDisposable接口。该实现存在,但该对象无法投射到IDisposable。尽管如此,我最终从HierarchicalLifetimeManager派生出了ContainerTrackedTransientLifetimeManager,以便在处理子容器时处理实例。 – PVitt 2011-05-06 09:59:15

+0

我认为这个想法是正确的,但是如果不修补Unity本身就无法完成。如果我从HierarchicalLifetimeManager派生,我的SynchronizedGetValue方法不会被调用。如果我派生自ContainerControllerLifetimeManager,则在处理子容器时不会处理该实例。因此我需要一个定制的BuilderStartegy。但是这需要访问LifetimeManager的Internel字段IsUsed。所以我担心这不能在大会之外实现。 – PVitt 2011-05-06 11:29:20