ViewModel lifetime issue

Sep 27, 2010 at 10:39 PM

I am trying to see why my view model does not get disposed, even if all the references to it from my code should be gone. 

Using WinDbg to track existing references I found the following:

> !gcroot -nostacks 07700740DOMAIN(0726C1C8):HANDLE(Pinned):3a01dd4:Root:0867b910(System.Object[])->07681f20(System.ComponentModel.Composition.Hosting.CompositionContainer)->07682280(System.Collections.ObjectModel.ReadOnlyCollection`1[[System.ComponentModel.Composition.Hosting.ExportProvider, System.ComponentModel.Composition]])

->0768226c(System.Object[])->07681d60(MEFedMVVM.ViewModelLocator.MEFedMVVMExportProvider)->

07681d78(System.ComponentModel.Composition.Hosting.CatalogExportProvider)->076823c0(System.ComponentModel.Composition.Hosting.ImportEngine)->076823f8(Microsoft.Internal.Collections.ConditionalWeakTable`2[[System.ComponentModel.Composition.Primitives.ComposablePart, System.ComponentModel.Composition],[System.ComponentModel.Composition.Hosting.ImportEngine+PartManager, System.ComponentModel.Composition]])->07682408(System.Collections.Generic.Dictionary`2[[System.Object, mscorlib],[System.ComponentModel.Composition.Hosting.ImportEngine+PartManager, System.ComponentModel.Composition]])->076fe5f4(System.Collections.Generic.Dictionary`2+Entry[[System.Object, mscorlib],[System.ComponentModel.Composition.Hosting.ImportEngine+PartManager, System.ComponentModel.Composition]][])->076fe560(System.ComponentModel.Composition.Hosting.ImportEngine+PartManager)->076fe490(System.ComponentModel.Composition.ReflectionModel.DisposableReflectionComposablePart)->07700740(Test.ViewModels.BarViewModel)

So it seems that either MEF or MEFedMVVM or both are the culprits.

I know that it is possible that MEFedMVVM design parameters didn't include guaranteed disposal of VMs. However, I would be grateful for a suggestion on what approach to take and what may be done to avoid VM references that are collected only when application exits.

Coordinator
Sep 27, 2010 at 10:48 PM

how are u importing the ViewModel... there are 3 options which are explained here..

http://marlongrech.wordpress.com/2010/08/05/adding-more-goodies-in-mefedmvvm/ in the section - Added 2 new attached properties to make things more clear.

Sep 27, 2010 at 11:02 PM

I specify creation policy explicitly:

    [PartCreationPolicy(CreationPolicy.NonShared)]
    [ExportViewModel("CustomBarViewModel")]
    public class BarViewModel : ViewModelBase
ViewModelBase is Cinch V2 class

Coordinator
Sep 27, 2010 at 11:04 PM

try using the new Attached Property NonShareViewModel and see how that works out for u

Oct 5, 2010 at 10:53 PM
Edited Oct 6, 2010 at 2:30 AM

No difference with the new attached property Marlon.

I pursued a closely related issue on MEF discussion list. You would oblige me if you could find the time to check the thread below and comment on it :

http://mef.codeplex.com/Thread/View.aspx?ThreadId=228535

Or maybe you don't need to read the entire thread and it will be enough to quote the last post which says:

<quote>

.... I just had a quick look at MEFedMVVMExportProvider at http://mefedmvvm.codeplex.com/SourceControl/changeset/view/60612#1279042 and it is where we are loosing the information.

        protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
        {
            var exports = _exportProvider.GetExports(definition, atomicComposition);
            return exports.Select(export => new Export(export.Definition, () => GetValue(export)));
        }

 If this guy is going to wrap the Export then it needs to have a disposable Export and call dispose on the export it is wrapping in order for the full disposal to work.

</quote> 

Thanks!

Dec 30, 2010 at 7:29 AM

Any solution on that? It does't get garbage collected because the ViewModelBase of cinch v2 implements IDisposable.
So the mef will holds a reference on it, till the export will be disposed. But doen't realy undestand what they are meaning with have a dispsoable export to get that working.

Could you look at this again or give some advice marlon?

Dec 31, 2010 at 4:59 PM

Helmut/Tonko this looks like something I will have to fix in Cinch. Looks like I should not have implemented IDisposable. I will change that to some other interface, that people can call themselves.

Jan 1, 2011 at 8:11 AM

 

I have now fixed all this I feel. See latest release and the notes associated with it.

Popups/ChildWindows

I have tested it for SL/WPF and see all popups being disposed (when the GC sees fit though, so may have to open a few popups in order for GC to actually collect)

MEF and IDisposable

You were correct about my being dumb implementing IDisposable, I have swapped that for new CinchV2 interface called ICinchDisposable, which you should call when your Views deconstructor is called. Again Views deconstructor is called when GC sees fit

Jan 1, 2011 at 8:11 AM

 

I have now fixed all this I feel. See latest release and the notes associated with it.

Popups/ChildWindows

I have tested it for SL/WPF and see all popups being disposed (when the GC sees fit though, so may have to open a few popups in order for GC to actually collect)

MEF and IDisposable

You were correct about my being dumb implementing IDisposable, I have swapped that for new CinchV2 interface called ICinchDisposable, which you should call when your Views deconstructor is called. Again Views deconstructor is called when GC sees fit

Jan 11, 2011 at 12:14 AM
sachabarber wrote:

 

I have now fixed all this I feel. See latest release and the notes associated with it.

Popups/ChildWindows

I have tested it for SL/WPF and see all popups being disposed (when the GC sees fit though, so may have to open a few popups in order for GC to actually collect)

MEF and IDisposable

You were correct about my being dumb implementing IDisposable, I have swapped that for new CinchV2 interface called ICinchDisposable, which you should call when your Views deconstructor is called. Again Views deconstructor is called when GC sees fit

The memory leak that I am experiencing is currently not a huge problem, so I was not rushing this, as I have other more pressing issues.

I'll test the latest combination of Cinch with MefedMvvm ASAP and let you know.

In any case, thanks Sacha!

Jan 11, 2011 at 10:18 PM
Edited Jan 11, 2011 at 11:51 PM
HelmutE wrote:

Any solution on that? It does't get garbage collected because the ViewModelBase of cinch v2 implements IDisposable.
So the mef will holds a reference on it, till the export will be disposed. But doen't realy undestand what they are meaning with have a dispsoable export to get that working.

Could you look at this again or give some advice marlon?

Upon rereading MEF-related thread I am not sure this is really Sacha's problem. Why should he not implement IDisposable if he wishes? He doesn't have apriori knowledge that his code would be used with MEF and shouldn't have to care.

What I think they are meaning with have a disposable export is for Marlon to create a class like:

 

class MefedMvvmExport : Export, IDisposable

{

	public void Dispose()

	{

		IDisposable vd = Value as IDisposable;

		if(vd!=null)

		{

			vd.Dispose();

		}

	}

}

 

 

and then for him to do:

return exports.Select(export => new MefedMvvmExport (export.Definition, () => GetValue(export)));

instead of just new Export(export.Definition, () => GetValue(export)));

Namely, I understand ' If this guy is going to wrap the Export then it needs to have a disposable Export and call dispose on the export it is wrapping' as referring to this code:

new Export(export.Definition, () => GetValue(export)));

I really intended to test this understanding myself, before continuing to ask Marlon (if at all) but I was pressed for time too.

I believe (still have to test it) that Sacha's solution would work too (as a matter a fact, I think I was sarcastic in MEF thread when I mentioned not implementing IDisposable as a solution)  but it is not the right solution, IMHO, considering all that was said in the other thread. Really, why should some MVVM framework not implement IDisposable in some class only because of the existence of MEF?

Coordinator
Jan 12, 2011 at 2:38 PM

MEF will keep an object in memory if it resolves it and the object implement IDisposable... it does this so that when you call the Dispose on the container it calls the dispose on these objects. Problem is that you never really dispose the container thus the object just stays in memory. 

Jan 12, 2011 at 11:01 PM
Edited Jan 13, 2011 at 3:45 AM
marlongrech wrote:

MEF will keep an object in memory if it resolves it and the object implement IDisposable... it does this so that when you call the Dispose on the container it calls the dispose on these objects. Problem is that you never really dispose the container thus the object just stays in memory. 

I guess that was so in MEF versions before Preview 9.

Since then MEF team has apparently realized that mandatory disposal of the container is not an acceptable solution in some, not so rare, scenarios. They added ReleaseExport call. There are several discussions regarding cleanup without disposing the container on MEF site, including the one started by me.

Link to the latter one : http://mef.codeplex.com/Thread/View.aspx?ThreadId=228535

In my post above I quoted a part of MEF developer Wes Haggard's reply to me.

Below in bold is the full quote of that reply, followed by the excerpt from MEF source code with the comments in green that appear to say the same thing in a different way.

While it is true that the container will hold references to Disposable NonShared parts the way to clean those up, assuming you don't want to dispose the container, is to call ReleaseExport. Which is where I think we started this thread at. If ReleaseExport is not doing what it isn't supposed to then there is a bug somewhere.

I'm not familar with MEFedMVVM but in your debug output above I see MEFedMVVMExportProvider, which could potentially cause the problem. Do if that export provider is wrapping the exports coming from the CatalogExportProvider? If it is then it could be the broken link.

.... I just had a quick look at MEFedMVVMExportProvider at http://mefedmvvm.codeplex.com/SourceControl/changeset/view/60612#1279042 and it is where we are loosing the information.

        protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
        {
            var exports = _exportProvider.GetExports(definition, atomicComposition);
            return exports.Select(export => new Export(export.Definition, () => GetValue(export)));
        }

 If this guy is going to wrap the Export then it needs to have a disposable Export and call dispose on the export it is wrapping in order for the full disposal to work.

Preview 9 and the latest MEF 2 Preview 2 contain this (and other similar) code in CompositionContainer.cs.

        /// <summary>
        ///     Releases the <see cref="Export"/> from the <see cref="CompositionContainer"/>. The behavior
        ///     may vary depending on the implementation of the <see cref="ExportProvider"/> that produced 
        ///     the <see cref="Export"/> instance. As a general rule non shared exports should be early 
        ///     released causing them to be detached from the container.
        ///
        ///     For example the <see cref="CatalogExportProvider"/> will only release 
        ///     an <see cref="Export"/> if it comes from a <see cref="ComposablePart"/> that was constructed
        ///     under a <see cref="CreationPolicy.NonShared" /> context. Release in this context means walking
        ///     the dependency chain of the <see cref="Export"/>s, detaching references from the container and 
        ///     calling Dispose on the <see cref="ComposablePart"/>s as needed. If the <see cref="Export"/> 
        ///     was constructed under a <see cref="CreationPolicy.Shared" /> context the 
        ///     <see cref="CatalogExportProvider"/> will do nothing as it may be in use by other requestors. 
        ///     Those will only be detached when the container is itself disposed.
        /// </summary>
        /// <param name="export"><see cref="Export"/> that needs to be released.</param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="export"/> is <see langword="null"/>.
        /// </exception>
        [SuppressMessage("Microsoft.Performance", "CA1822")]
        public void ReleaseExport(Export export)
        {
            Requires.NotNull(export, "export");

            IDisposable dependency = export as IDisposable;

            if (dependency != null)
            {
                dependency.Dispose();
            }
        }

Check also stackoverflow answer by Wim Coenen:

http://stackoverflow.com/questions/4368217/object-destruction-problem-with-mef/4368737

I'm hoping that the information, the links and the sources listed so far are relevant.

This is not easily understood problem, as far as I can tell. 

Coordinator
Jan 13, 2011 at 8:46 PM

mmm that is true I am wrapping the exports in MEFedMVVM. I was not aware of this to tell you the truth. I can see that the exports coming are CatalogExport yet this class is internal... do you know of an implementation of an Export that has a Dispose that I can use?

 

Jan 13, 2011 at 11:30 PM
Edited Jan 14, 2011 at 5:02 AM

Off hand I don't know of any.

I'll ask MEF guys on their discussion list.

Until they respond let me ask : is copy construction of new export objects in your Select clause really needed? That is, why not just:

     protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
     {

         return _exportProvider.GetExports(definition, atomicComposition);

     }

 Alternatively, if copy-construction is required, something along the following lines may be a way to go:

class MefedMvvmExport : Export, IDisposable
{
  Export _wrapped;

  public MefedMvvmExport(Export wrapped) : base(wrapped.Definition(), () => GetValue(wrapped))
  {
    _wrapped=wrapped;
  }


  public void Dispose()
  {
    IDisposable id = _wrapped as IDisposable;
    if(id!=null)
    {
      id.Dispose();
    }
  }
}

then

return exports.Select(export => new MefedMvvmExport (export));

 

Feb 28, 2011 at 1:18 AM
Edited Feb 28, 2011 at 1:19 AM

Hmmm, no luck on MEF Codeplex discussion list. I bumped my post to the top, but after more than a month it seems that no MEF or Microsoft insider is following that site and answering the questions anymore. I think Marlon that you have a much better chance of getting somewhere, especially since I read in WPF Disciples group that Glen Block himself is talking about MEFedMVVM in some speeches he is giving around the world.  That's cool and congrats on it!

May 8, 2012 at 7:33 AM

HI,

Have you found a solutions for this issue, if you have then please share with us, as i am facing the same issue as the multiple objects are being created for the Models and Views and are not being disposed until the application got closed.

Coordinator
May 8, 2012 at 7:34 AM

Are you using the ViewModelLocator.NonSharedViewModel="xxx" ?

 

Did you try to do [PartCreationPolicy(CreationPolicy.NonShared] on your ViewModel?

May 8, 2012 at 8:23 AM

Hi, What my Scnario is that i am using the Main View Model for the Main Window, and then All the other forms are used as User Controls which are loaded in the MainWindow, To Locate and Load the Views I am using the ViewResolver from the Cinch framework which in the end is using the MFEDMVVM to load the Views and View Models, 

 

Most of my ViewModels are decorated like 

[ExportViewModel("TestViewModel")] // View ModelName

[PartCreationPolicy(CreationPolicy.NonShared)]

Now if i put a code like 

   System.Diagnostics.Debug.WriteLine("Destroying ViewModel");
in the Destructors, and then then click the Button on the Menu Multiple times, then Multiple instances of the ViewModel are being created and not destroyed unless i close the application, which will print multiple lines for 

Destroying ViewModel
in the Debug Window.
Coordinator
May 8, 2012 at 8:28 AM
did you try using a Memeory profiler? usually destructors are a bit mumbo jumbo. try this one memprofiler.com

Regards
Marlon
WPF Blog - http://marlongrech.wordpress.com/
MEFedMVVM - http://mefedmvvm.codeplex.com


On Tue, May 8, 2012 at 10:23 AM, asifch <notifications@codeplex.com> wrote:

From: asifch

Hi, What my Scnario is that i am using the Main View Model for the Main Window, and then All the other forms are used as User Controls which are loaded in the MainWindow, To Locate and Load the Views I am using the ViewResolver from the Cinch framework which in the end is using the MFEDMVVM to load the Views and View Models,

Most of my ViewModels are decorated like

[ExportViewModel("TestViewModel")] // View ModelName

[PartCreationPolicy(CreationPolicy.NonShared)]

Now if i put a code like

   System.Diagnostics.Debug.WriteLine("Destroying ViewModel");
in the Destructors, and then then click the Button on the Menu Multiple times, then Multiple instances of the ViewModel are being created and not destroyed unless i close the application, which will print multiple lines for

Destroying ViewModel
in the Debug Window.

Read the full discussion online.

To add a post to this discussion, reply to this email (MEFedMVVM@discussions.codeplex.com)

To start a new discussion for this project, email MEFedMVVM@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


May 8, 2012 at 8:28 AM

If you want i can create a sample application and then you can look into it in detail.

Coordinator
May 8, 2012 at 8:30 AM
yes please

Regards
Marlon
WPF Blog - http://marlongrech.wordpress.com/
MEFedMVVM - http://mefedmvvm.codeplex.com


On Tue, May 8, 2012 at 10:28 AM, asifch <notifications@codeplex.com> wrote:

From: asifch

If you want i can create a sample application and then you can look into it in detail.

Read the full discussion online.

To add a post to this discussion, reply to this email (MEFedMVVM@discussions.codeplex.com)

To start a new discussion for this project, email MEFedMVVM@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Sep 6, 2013 at 2:13 AM
Edited Sep 6, 2013 at 2:14 AM
Did this issue ever get resolved?

I ask as I've noticed this leaking of ViewModels problem in my current project that uses Cinch and MEFedMVVM. After reading above I've changed it all so my (nonshared) ViewModels no longer implement IDisposable (this seems very odd). Instead, they call ICinchDisposable.Dispose on themselves when they detect their injected View is closed, or for ViewModel first scenarios, I wrap all the instantiated ViewModels in my own IDisposable 'ViewModelDisposer' class to allow me to use 'Using' on them. But, I feel that both techniques are rather clunky.

I noticed you mention using the ReleaseExport method for the Export, but I'm a bit confused on how this should work.