How to revmove registered instance

Mar 1, 2013 at 8:06 AM
Rcently, We have met a memory leak issue which is cause by the screen is remvoed from main from but still referenced by UnityContainer. The following is the sample code
private void btnEvent_Click(object sender, EventArgs e)
{
    if (!_container.IsRegistered(typeof(EventForm), "EventForm"))
    {
        var subForm = new EventForm(this);
        _container.RegisterInstance("EventForm", subForm);
    }

    var eventForm = _container.Resolve<EventForm>("EventForm");
    eventForm.Show(); 
}
After close the eventForm, I can see the eventForm is till stick in memoy and referened by the UnityContainer via WinDbg, even I have forced GC.

My question is: is there any way to unregister the eventForm instance? So i can call it in some place like the close event of EventForm.
Thanks in advance.

Regards,
Anders
Mar 1, 2013 at 8:50 AM
If you use the ExternallyControlledLifetimeManager (see Understanding Lifetime Managers) then Unity will only maintain a weak reference allowing GC to collect the object when it is no longer used.

Another approach would be to specify a ContainerControlledLifetimeManager when registering and then use the RemoveValue method on that LifetimeManager instance when done.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Mar 1, 2013 at 9:14 AM
Thank for your so quick reply.

Yes, the ExternallyControlledLifetimeManager can resolve the memory issue, due to it use the weakreference. However if you execute the btnEvent_Click once more, the disposed eventForm can be returned by UnityContainer (GC hasn't triggered and eventForm is still in memory) and cause further problem.

About the secnod solution, i just have a try. When the first time open and close the eventForm, there is no problem, i could see the RemoveValue method on that LifetimeManager instance is execute. However, for the second time, when close the eventForm, the RemoveValue() doesn't execute. At the 3rd time, the disposed form is returned by UnityContainer and cause the some issue in solution #1.
The following is my test code, am I miss something?
public EventForm(MainForm mainForm)
{
    InitializeComponent();

    mainForm.OpacityChanged += mainForm_OpacityChanged;
    FormClosing += delegate
        {
            if (chkUnsubscribe.Checked)
                mainForm.OpacityChanged -= mainForm_OpacityChanged;

            var registration = mainForm._container.Registrations.FirstOrDefault(r => r.RegisteredType == this.GetType());
            if (registration != null)
                registration.LifetimeManager.RemoveValue();
  
        };
}
Mar 1, 2013 at 3:03 PM
Maybe you can expand on the exact behavior you want to achieve. In general, you don't want to pass around an instance of the container and check to see if types are registered. You would usually perform all registrations at application startup.

It looks like you have a UI application and that you are registering EventForm as a singleton. When you register a singleton it is around for the lifetime of the application so it would not actually be what I would call a memory leak.

Is there a specific reason why you only need one instance of EventForm? Can you just inject an instance of EventForm and store it in a static variable to eliminate the need for always checking the container? Or is it possible to just retrieve a new instance of EventForm when required (instead of a singleton)?

Another potential approach is to use a child container. Or also to create a custom LifetimeManager.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Mar 4, 2013 at 3:03 AM
The EventForm is not actually act as a sigleton. It's a view hosted in TabControl which is placed in Main Window. In our application, we have ribbon and several views, say 'View A', 'View B' and so on...
When Click the 'View A' button in ribbon, we will use the UnityContainer to resolve it (if not exist) and add it to the main Tab Control. If the 'View A' is already shown in the main Tab Control, we will find it and switch to it. That's the reason we use the following code
 private void btnEvent_Click(object sender, EventArgs e)
{
    if (!_container.IsRegistered(typeof(EventForm), "EventForm"))
    {
        var subForm = new EventForm(this);
        _container.RegisterInstance("EventForm", subForm);
    }

    var eventForm = _container.Resolve<EventForm>("EventForm");
    eventForm.Show(); 
}
We first check whether there is an EventForm instance or not via UnityContainer::IsRegistered, then resolve the view by UnityContainer. We could not simple use _container.Resolve<EventForm>(), because call it twice, the UnityContainer will return different instances.

Our's problem is how to remove the view instance from UnityContainer, when the view is removed from the main Tab Control. We wouldn't want to reuse the original crated view, and want to recreate a new one.

Regarding your suggestion #2 custom LifetimeManager. Do you mean i should refer to Brad's solution? Honestly, i don't understand his soltuion very well. What's the useful of IObjectBuilder interface, and who should implement this interface. Could you give me some instrument here? Thanks in advance.

Regards,
Anders
Mar 4, 2013 at 5:06 AM
Edited May 1, 2013 at 8:17 PM
In Unity there is a difference between registration and object lifetime. Whether an object is registered or not does not indicate whether or not it has ever been instantiated or what the object lifetime is.

Based on what you wrote probably the easiest approach would be use a child container to store the transient EventForm. Although this approach could cause other headaches depending on what the overall design is.

So you could do something like this:
// At startup
_parentContainer = new UnityContainer();
// Register classes etc.

// Create child container for transient registrations
_container = _parentContainer.CreateChildContainer();

private void btnEvent_Click(object sender, EventArgs e)
{
if (!_container.IsRegistered(typeof(EventForm), "EventForm"))
{
    var subForm = new EventForm();
    _container.RegisterInstance("EventForm", subForm);
}

var eventForm = _container.Resolve<EventForm>("EventForm");

#region forTestingInstance
            
if (eventFormInstance != null)
{
    bool isSameReference = ReferenceEquals(eventFormInstance, eventForm);
}
eventFormInstance = eventForm;
            
#endregion

eventForm.Show();
}

private EventForm _eventFormInstance;
private void btnEvent_Click(object sender, EventArgs e)
{
    if (!_container.IsRegistered(typeof(EventForm), "EventForm"))
    {
        var subForm = new EventForm();
        _container.RegisterInstance("EventForm", subForm);
    }

    var eventForm = _container.Resolve<EventForm>("EventForm");

    #region forTestingInstance
            
    if (_eventFormInstance != null)
    {
        System.Diagnostics.Debug.Assert(ReferenceEquals(_eventFormInstance, eventForm));
    }
    _eventFormInstance = eventForm;
            
    #endregion

    eventForm.Show();
}

// Later on when you need to remove registration Dispose container and create new container
private void RemoveView()
{
    _container.Dispose();
    // new container will not have EventForm registered
    _container = _parentContainer.CreateChildContainer();
}

If the child container is a bit harsh (and causes other issues) you could use a factory to either return null or an object instance. If the object is null then you need to create a new instance:
_container = new UnityContainer();
// register classes etc.

RegisterEventFormNull<EventForm>("EventForm");

//...

private EventForm _eventFormInstance;

private void RegisterEventFormNull<TFROM>(string name)
{
    _eventFormInstance = null;
    var registration = _container.Registrations.FirstOrDefault(r => r.Name == name && r.RegisteredType == typeof(TFROM));

    if (registration != null)
    {
        registration.LifetimeManager.RemoveValue();
    }

    _container.RegisterType<EventForm>("EventForm",
        new ContainerControlledLifetimeManager(),
        new InjectionFactory((c) =>
        {
            return null;
        }
    ));
}

private void btnEvent_Click(object sender, EventArgs e)
{
    if (_container.Resolve<EventForm>("EventForm") == null)
    {
        var subForm = new EventForm();
        _container.RegisterType<EventForm>("EventForm", 
            new InjectionFactory( (c) =>
            {
                return subForm;
            }
        ));
    }

    var eventForm = _container.Resolve<EventForm>("EventForm");

    #region forTestingInstance

    if (_eventFormInstance != null)
    {
        System.Diagnostics.Debug.Assert(ReferenceEquals(_eventFormInstance, eventForm));
    }
    _eventFormInstance = eventForm;

    #endregion

    eventForm.Show();
}

This could be improved with some more abstraction (extension methods might be nice), better naming etc.

Now to be honest, what you describe sounds like a bit of an abuse of the container to me. It would be preferable for the application code to not know about Unity if possible. In that approach you configure Unity to inject an EventForm factory into your application (similar to above). When you need an EventForm retrieve it from the factory. When you are done with that EventForm, remove it from the tab and Dispose it. The next time you need an EventForm get another instance from the factory. Now you will have to manage whether you have added the EventForm or not (instead of using the container) but that seems like something the application should be managing.


~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to