Are MEF exports cached or discovering every time on request?
will MEF do global lookup again or it caches somewhere internally
Yes, MEF perfoms some caching and widely uses lazy initialization, if you question is about MEF performance:
1) metadata (composable parts, export definitions and import definitions) is cached. Example:
public override IEnumerable<ExportDefinition> ExportDefinitions{ get { if (this._exports == null) { ExportDefinition[] exports = this._creationInfo.GetExports().ToArray<ExportDefinition>(); lock (this._lock) { if (this._exports == null) { this._exports = exports; } } } return this._exports; }}
2) exported values are cached too:
public object Value{ get { if (this._exportedValue == Export._EmptyValue) { object exportedValueCore = this.GetExportedValueCore(); Interlocked.CompareExchange(ref this._exportedValue, exportedValueCore, Export._EmptyValue); } return this._exportedValue; }}
Of course, when using CreationPolicy.NonShared
, exported value becomes created again and again, when you requesting it. But even in this case "global lookup" isn't performed, because metadata is cached anyway.
It does a lookup every time, when you use [PartCreationPolicy(CreationPolicy.NonShared)]
. You then have to implement the caching yourself.
The default implementation is using a Singleton pattern. This equals the attribute [PartCreationPolicy(CreationPolicy.Shared)]
. This is the best practice.
For more information, read http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/01/09/mef-for-beginner-part-creation-policy-part-6.aspx
Although the values/metadata might be partially cached, doing some performance testing shows that some lookup is performed every time a call to GetExportedValue
is made. So if you have many calls where you need to get the value, you should do the caching yourself.
namespace MEFCachingTest{ using System; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.ComponentModel.Composition.Primitives; using System.Diagnostics; using System.Reflection; public static class Program { public static CompositionContainer Container { get; set; } public static ComposablePartCatalog Catalog { get; set; } public static ExportedClass NonCachedClass { get { return Container.GetExportedValue<ExportedClass>(); } } private static ExportedClass cachedClass; public static ExportedClass CachedClass { get { return cachedClass ?? (cachedClass = Container.GetExportedValue<ExportedClass>()); } } public static void Main() { Catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); Container = new CompositionContainer(Catalog); const int Runs = 1000000; var stopwatch = new Stopwatch(); // Non-Cached. stopwatch.Start(); for (int i = 0; i < Runs; i++) { var ncc = NonCachedClass; } stopwatch.Stop(); Console.WriteLine("Non-Cached: Time: {0}", stopwatch.Elapsed); // Cached. stopwatch.Restart(); for (int i = 0; i < Runs; i++) { var cc = CachedClass; } stopwatch.Stop(); Console.WriteLine(" Cached: Time: {0}", stopwatch.Elapsed); } } [Export] [PartCreationPolicy(CreationPolicy.Shared)] public class ExportedClass { }}
For more variations, look at this gist: https://gist.github.com/DanielRose/d79f0da2ef61591176ce
On my computer, Windows 7 x64, .NET 4.5.2:
Non-Cached: Time: 00:00:02.1217811 Cached: Time: 00:00:00.0063479
Using MEF 2 from NuGet:
Non-Cached: Time: 00:00:00.2037812 Cached: Time: 00:00:00.0023358
In the actual application where I work, this made the application 6x slower.