Oct 2, 2010 at 10:31 PM
Edited Oct 4, 2010 at 7:45 PM
Thanks for weighing in Wes.
I should have probably given all the details up front. However, when several libraries are involved (MEF and another fine piece of software i.e: http://mefedmvvm.codeplex.com/) I tend to first try and
get responses to narrowly targeted questions to both sides, and then use them to hopefully figure out the correct way to proceed. After the initial post, debugging the root references seems to show that view is not collected because its view model reference
is held by MEF, rather than MEF holding a direct reference to a view export.
Here is WinDbg GCRoot command result on view model (of Bar view) instance address and then I will answer your questions:
> !gcroot -nostacks 07700740DOMAIN(0726C1C8):
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]])->
Since BarViewModel and Bar have references to each other, unless both are not reachable via some GC root, both will remain uncollected. When a view gets created, its VM or BarViewModel is implicitly imported by MEFedMVVM infrastructure because view
XAML contains a special attached property. This property will make MEFedMVVM instantiate (non-Shared) BarViewModel via MEF and attach it to a view as its XAML DataContext.
Answers to questions:
1) I use DirectoryCatalog and never explicitly call Compose on the container. Since I am explicitly importing a view, which has said MEFedMVVM attached property to import BarViewModel, Compose may be happening in there.
2) The way I am getting exports is because I wanted to be able to see all the exports and their metadata that were found in DirectoryCatalog. Directory will contain a bunch of Dlls, all exporting a "Bar" contract on some WPF UserControl, but it
is attributed with other metadata as well. So initially it was for debugging purposes. Once I enumerate through the exports I really need only one specific Bar view which has certain metadata value. The second reason for using this overload is that it returns
IEnumerable of Exports, rather than Lazy<Bar> and I can call ReleaseExport on the former but not on the latter.
3) Bar view reference is assigned to a WPF Content property of ContentControl which is a singleton and alive as long as App is alive. IOW:
when I find the right view from GetExports IEnumerable I do:
Bar bar = (from export in exports
where export.Metadata[key] == ...
ContentControl.Content = bar.Value;
Release all exports;
I was considering nulling the Content property before overwriting it with a new Bar reference, moving GC.Collect() elsewhere, etc. But after a dozen of calls to the code as above, each call instantiating a different Bar view, all of them are still in
memory, so I don't think it matters. I use Debug trace in finalizer to detect when view and view model get collected. It happens only when container is disposed.
So now the problem is understanding what causes MEF to hold a reference to a BarViewModel, as gcroot shows above. If it is due to something in MEFedMVVM implementation of ExportProvider, what should I look for before I contact the author of the library?
I am hoping if BarViewModel would get collected, it is the only object holding a reference to a Bar view so Bar would then get collected as well.