1
votes

I have a WPF view that has a corresponding ViewModel. All instances are resolved via an unity container. Because I'm using prism I need two independent instances of the view to add it into two different regions the view is registered to. If I'd try to add one instance into both regions I get an

InvalidOperationException: Specified element is already the logical child of another element. Disconnect it first.

when the view is added into the second region because it is already added to the first region.

This problem can easily be solved by using a TransientLifetimeManager that always returns a new instance so both regions would be filled with an independent instance.

But we have decided to create a child container when a new user logs on. Every session related view and view model are resolved using this child container. When the user's session ends, the child container is disposed so that also every session related instances are disposed. But using a TransientLifetimeManager the unity container cannot dispose those instances.

What we need is a lifetime manager that always returns a new instance, but is also capable of disposing those instances. Is there already such an lifetime manager around? Or is there another way to achieve what I described above?

2
For those reading the answers: "making eligible to be GC'ed" does not "imply calling Dispose [immediately, or ever technically]". For strict scopes/lifetimes this is a big difference.user2864740

2 Answers

2
votes

What you want sounds like a variant of the ContainerControlledLifetime manager that does not maintain a singleton instance, but a collection of instances. Unfortunately this is not one of the built-in lifetime managers.

You can look at the code for the ContainerControlledLifetimeManager and see that it is pretty simple. Your "SynchronizedGetValue" implementation would always return null (signaling to the container that a new instance needs to be instantiated). You could just subclass ContainerControlledLifetimeManager and override that method.

I've pretty much written it. I suppose I could give you the code. :)

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

That should work. I've not tested it... from the interface, it looks like it's designed for a 1 to 1 LifetimeManager to Object relationship, but if it turns out it is more than that, you might have to override SetValue (adds to a collection of objects) and dispose (disposes that collection of objects). Here's that implementation:

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();
    }

I'm not sure which of these is the right answer. Let me know how it goes for you.

1
votes

When you use transient lifetime manager (which is the default), Unity does not keep a reference to the created instance.

Thus, when there are no more reference to the instance, it will be GCed.