1

I'm creating an application with multiple plugins using MEF2 (Microsoft.Composition). These plugins should import some common object and they should all share the same instance of this object... so a typical Singleton.

However, when I [Import] this common object into my plugins they all get their own copy instead of a shared one.

In .NET Framework MEF1, all object were created as singletons by default. This does not seem to be the case for .NET Core MEF2.

How can I make sure that all my plugins get the same singleton instance of my common object?

Sample code

Startup

static void Main(string[] args) {
    ContainerConfiguration containerConfig = new ContainerConfiguration()
        .WithAssembly(Assembly.GetExecutingAssembly())
        .WithAssembly(typeof(ICommonObject).Assembly);

    using (CompositionHost container = containerConfig.CreateContainer())             {
        _mainApp = container.GetExport<MainApp>();
        _mainApp.Start();
    }
}

MainApp

[Export(typeof(MainApp))]
public class MainApp {
    [Import] public ICommonObject CommonObject { get; set; }
    [ImportMany] public IEnumerable<IPlugin> Plugins { get; set; }

    public void Start() {
        CommonObject.SomeValue = "foo";
        Console.WriteLine("SomeValue (from MainApp): " + CommonObject.SomeValue);

        foreach (IPlugin plugin in Plugins) {
            plugin.Start();
        }
    }
}

Plugin

[Export(typeof(IPlugin))]
public class SomePlugin : IPlugin {

    [Import] public ICommonObject CommonObject { get; set; }

    public void Start() {
        Console.WriteLine("SomeValue (from plugin): " + CommonObject.SomeValue);
    }
}

Output

SomeValue (from MainApp): foo
SomeValue (from plugin):
TheHvidsten
  • 4,028
  • 3
  • 29
  • 62

1 Answers1

2

After much trial and error I seem to have finally found a solution myself.

The trick seems to be to use ConventionBuilder. This has an extension method called .Shared() which makes all objects derived from a specific type into a Singleton.

For my code examples, just add the following to the top of the Startup code:

ConventionBuilder conventions = new ConventionBuilder();
conventions.ForTypesDerivedFrom<ICommonObject>()
    .Export<ICommonObject>()
    .Shared();

ContainerConfiguration containerConfig = new ContainerConfiguration()
    .WithAssembly(Assembly.GetExecutingAssembly(), conventions);

For some reason, the object implementing ICommonObject doesn't even need an [Export] attribute. In any case, the output from the example is now:

SomeValue (from MainApp): foo
SomeValue (from plugin): foo
TheHvidsten
  • 4,028
  • 3
  • 29
  • 62