MEF - or Managed Extensibility Framework - is Microsoft's official Inversion Of Control (IoC) and Dependency Injection (DI) framework.
Although MEF is not a true DI container, it has a lot of features compared to many other IoC frameworks. However, MEF is often measured
as a slow IoC framework in tests and therefore is not very tempting to use. MEF on the other side is readily available in .NET.
If you use .NET 4.5, you will have access to MEF 2 features such as ExportFactory. The worst feature in MEF is arguable the way the IoC
container disposes objects. If you have a nonshared
Part or instance (i.e not a singleton, but a part you can instantiate multiple
instances of), and this instance type implements
IDisposable, most likely
this object will not be disposed until the container is teared down. This is not a desired feature, as object instances will accumulate and
memory consumption of your application using MEF will go up. With the aid of ExportFactory, we can be able to free up object instances on demand.
Let's use the
ServiceLocator to get our instances from ExportFactory. When you use ExportFactory, you call
CreateExport(), which
returns an ExportLifetimeContext<T>. This object can then be used to dispose the object. By disposing the ExportLifetimeContext<T>,
the object that was created with the ExportFactory is truly disposed and released at the same time from the IoC, finally making it possible to
gather with Garbage Collection (GC), in the end freeing up memory.
The code for retrieving the object instance via an ExportFactory can look like this:
[Export]
public class ExportFactoryProvider<T>
{
[Import]
public ExportFactory<T> Factory { get; set; }
}
Let's test out this
ExportFactoryProvider with service locator. First let's create a part we can test. Let's say we got a simple view model:
[Export(typeof(ITestViewModel))]
public class TestViewModel : ITestViewModel, IDisposable
{
public void SayHello(string message)
{
Debug.WriteLine(string.Format("Hi {0}!", message));
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool p)
{
Console.WriteLine("Disposed TestViewModel");
}
}
We can then create an instance of the TestViewModel using ServiceLocator using this:
var serviceFactory = ServiceLocator.Current.GetInstance<ExportFactoryProvider<ITestViewModel>>().Factory;
var testViewModelExport = serviceFactory.CreateExport();
MessageBox.Show("About to dispose an instance of ITestViewModel: ");
testViewModelExport.Value.SayHello("Johnny");
testViewModelExport.Dispose();
As you can see above, we must first use the ExportFactoryProvider, then call CreateExport, then use the
Value property and then use the
ExportLifetimeContext<T> to control the disposing of the object from the IoC container. But as you can see, we must do some juggling to
keep a reference to the lifetime context and the instance itself. These two objects are inherently tied together. Let's create a generic class to
keep them together.
[Export]
[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class ExportFactoryInstantiator<T> : IPartImportsSatisfiedNotification
{
[Import]
public ExportFactory<T> Factory { get; set; }
public T Instance { get; private set; }
private ExportLifetimeContext<T> lifeTime;
public void OnImportsSatisfied()
{
lifeTime = Factory.CreateExport();
Instance = lifeTime.Value;
}
public bool DisposeOnDemand()
{
if (lifeTime == null)
return false;
lifeTime.Dispose();
return Instance == null;
}
}
You can use the class above, ExportFactoryInstantiator to create NonShared parts (object instances) via ExportFactory and via the instance created
access the Instance property to get to the actual object and use the method DisposeOnDemand, which returns true if the object was set to null during the
execution of this method. You can also see that we use
IPartImportsSatisfiedNotification to await the resolution.
var anotherTestViewModel = ServiceLocator.Current.GetInstance<ExportFactoryInstantiator<ITestViewModel>>();
anotherTestViewModel.Instance.SayHello("Bob");
anotherTestViewModel.DisposeOnDemand();
Note that the instance now can be easily worked against. However, still it is a bit verbose, we can create a static method to help us with that:
public static ExportFactoryInstantiator<T> ResolveViaExportFactory<T>()
{
return ServiceLocator.Current.GetInstance<ExportFactoryInstantiator<T>>();
}
We have finally reduced the complexity of being able to dispose MEF parts on demand in our applications:
var yetAnotherTestViewModel = ResolveViaExportFactory<ITestViewModel>();
yetAnotherTestViewModel.Instance.SayHello("Joey");
yetAnotherTestViewModel.DisposeOnDemand();
To sum up, MEF 2 provides a feature where we can now dispose parts from the container on demand. This is a great improvement and will reduce the issues
around memory leaks substantially. If you have complex viewmodels, disposing a view model high up in the object graph will usually dispose all child view models, properties and so on and reduce memory usage. But always be careful when disposing objects from your container. You must be sure that the object will not be useful anymore.
In this StackOverflow thread you can also see an example of how to use a Part only when you need it, inside a using block:
Nicholas Blumhardt on using ServiceLocator and ExportFactory
As you can see, I use Nicholas Blumhardt's code as a starting point and refine it a bit to reduce complexity and tie together the Instance and provide DisponseOnDemand method. You must yourself judge when to release a part, be it a view model or some other resource. The using block are handy when you are sure you do not need the instance more than temporarily. You should consider doing that instead of importing many parts to your view model. Yes, the object creation of your view models will be quicker and yes, the execution will in fact be slower because of the need to create imported parts on demand via ServiceLocator, but at the same time, your application will keep a very low memory profile.
I haven't had the opportunity to test out the code above yet against production code, so do not use it in production without verifying its validity in for example Red Gate ANTS Memory Profiler and testing it. I would like to hear from other developers using MEF and have experienced its memory usage woes for more views about MEF. If you have also tips against efficiency and performance of MEF containers, please let us other MEF developers know.
Share this article on LinkedIn.
When to dispose parts in your application can follow different business rules. Many MEF developers create Web (MVC) or Client (WPF) clients using the MVVM pattern. If you for example create a view model to display a dialog, you can dispose the view model itself when the dialog is closed, if you however need to tell the consumer of the dialog which DialogResult was returned, you must be careful to dispose the dialog first when you really do not need it. That is just an example of how careful you must be when disposing objects. If you use ServiceAgents or proxies in your application, you can usually dispose these after using them inside using blocks. In the end, you end up with a check list of how to handle object disposing. I highly recommand Red Gate ANTS Memory Profiler to find out which objects have many instances of themselves. If you see these object types are accumulating, you should consider using the techiques above to free them from the container and reduce the memory footprint of your application.
ReplyDeleteHi, I'm struggling with MEF memory leaks in a large application, im finding it really difficult to follow what you have done here and apply this to my own situation,
ReplyDeleteI have updated the code to 4.5 to use the latest version of MEF, but the code didn't have a service locator before?, im not sure how to set that up in the context you have above.
Any chance you have a working sample that includes the code above?. would be greatly appreciated. thanks!
Hi, this example seems to be describing exactly what I need but it seems out of context. What(where) is ServiceLocator? And what code of yours replace what other code of yours, and where to put it... :/
ReplyDeleteIs there a running example available for this?
Sorry, I forgot that not all using MEF does not use a IoC framework. You could use Unity or Prism, preferably Prism 6. Github page: https://github.com/PrismLibrary/Prism
ReplyDeleteThe ServiceLocator is a pattern used in many libraries. I use MEF 2 in .NET 4.5 with Prism 4, building a composite application with WPF and MVVM pattern and leveraging it with Prism to have regions, view resolution, event aggregator and so on and also service locator.
Like the CommonServiceLocator in the following nuget package:
https://www.nuget.org/packages/CommonServiceLocator/
I have no running example of this as the source code of my application is far too extensive to include here.
What I have done is to make a small application with a basic Prism application from the Prism samples and add the code above and watch in Red Gate Ants Memory Profiler that object is finally disposed.