We're using MEF (.NET 4, can't use 4.5 at the moment) in a MVVM application. Everything was fine until we needed to create models on the fly, for instance editable rows of a table. I didn't want to run into memory leaks, I found this article http://pglazkov.blogspot.ch/2011/04/mvvm-with-mef-viewmodelfactory.html and I discovered an unexpected behavior that I would like to understand. This is an Item added to the Shell.Items observable collection:
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export]
public class Item : INotifyPropertyChanged, IDisposable
{
[Import]
private Lazy<Shell> shell;
/// <summary>
/// Initializes a new instance of the <see cref="Item"/> class.
/// </summary>
public Item()
{
this.Time = DateTime.Now;
}
~Item()
{
this.Dispose(false);
}
public event PropertyChangedEventHandler PropertyChanged;
public Shell Shell
{
get
{
return this.shell.Value;
}
}
public DateTime Time { get; private set; }
public void Initialize()
{
this.Shell.ItemsCount++;
}
public void Dispose()
{
this.Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
this.Shell.ItemsCount--;
}
}
[..]
}
And this is the factory:
[PartCreationPolicy(CreationPolicy.Shared)]
[Export]
public class ChildContainerItemFactory : ItemFactory
{
public override Item Create()
{
var container = ServiceLocator.Current.GetInstance<CompositionContainer>();
using (var childContainer = CreateTemporaryDisposableContainer(container))
{
var item = childContainer.GetExportedValue<Item>();
item.Initialize();
return item;
}
}
[..]
}
If I use this code, the Item is disposed together with the child container. If I change it to:
public override Item Create()
{
var container = ServiceLocator.Current.GetInstance<CompositionContainer>();
using (var childContainer = CreateTemporaryDisposableContainer(container))
{
var item = new Item();
childContainer.SatisfyImportsOnce(item);
item.Initialize();
return item;
}
}
The item is not disposed anymore with the container. I would like to understand if it is dangerous to use the GetExportedValue method (I use that method in other parts of the application) and which is the best practice to avoid memory leaks with for view models with a short lifetime.
Any help appreciated
As far as I know (from experimenting and looking at MEF's source code):
Now the difference between GetExportedValue and SatisfyImports is not the same. GetExportedValue returns all exported parts that are registered with the container. This includes the parts created by the container (the exported catalog parts mentioned in 1.) plus the parts registered using CompositionContainer.ComposeParts. SatisfyImports will inject any imports that are available but will not register the object as an export even if its class is marked as an exported type (see 1.). Additionally, SatisfyImports will disable recomposition, but this is off topic.
MEF's documentation on CodePlex provides valuable info on Parts Lifetime.