Are MEF exports cached or discovering every time on request?
Asked Answered
B

3

6

If I have one type MyClass, register with

[Export(typeof(Myclass))] attribute, and

[PartCreationPolicy(CreationPolicy.Shared)]

or

[PartCreationPolicy(CreationPolicy.NonShared)]

and later trying to call

compositionContainer.GetExportedValue<Myclass>() multiple times.

Question: with the first call, I will get my registered class via MEF - llokup all registered assemblies, then trying to find one registered contract. Question is about second time and so on - will MEF do global lookup again or it caches somewhere internally?

Bountiful answered 11/1, 2013 at 8:45 Comment(0)
H
7

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.

Honebein answered 11/1, 2013 at 9:36 Comment(1)
This is incomplete, in the sense that some lookup is still performed on every call. So doing client-side caching if there are many calls to GetExportedValue should be done.Ashurbanipal
R
0

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

Riviera answered 11/1, 2013 at 8:48 Comment(3)
"It does a lookup every time" - proof?Honebein
I think, that question isn't about caching concrete exported value.Honebein
Actually I want to know, if I widely use some [Import]MyClassObject in many views, how long it will be lookuped for the first&scond&so on time.Bountiful
A
0

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.

Ashurbanipal answered 12/12, 2014 at 14:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.