Search code examples
c#pluginsmefextensibility

Using MEF with C#, how do I call methods on the host, from the plugin?


I am trying to add plugin extensibility to my C# application using the Managed Extensibility Framework (MEF) framework, and so far it is going ok; I have my main/host application loading plugins from a defined folder, and can call their methods etc. from the main application. Both the host application and the plugins reference a seperate dll assembly which contains the interfaces common to all projects.

This is working fine and I can call/interact with the plugins from the main application. However, I also would like to be able to interact with the host application from the plugins, but can't seem to find out how this is done.

I would like to be able to get/set/execute exported properties and methods in the main app from my plugins. Currently I am only able to 'speak' to the plugins from the main app, not the other way around as well.

My code thus far:

Interface DLL

namespace MefContracts
{
    [InheritedExport]
    public interface IPlugin
    {
        String DoWork();
    }

    public class Class1
    {
        public IPlugin plugin { get; set; }
    }
}

Main/Host Application

namespace MyMEF
{
    class clsMEF
    {
        private CompositionContainer _container;

        [Import(typeof(MefContracts.IPlugin))]
        public MefContracts.IPlugin plugin;

        public clsMEF()
        {
            Compose();
        }

        void Compose()
        {
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new DirectoryCatalog("..\\..\\Extensions"));
            _container = new CompositionContainer(catalog);
            try
            {
                this._container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());
            }
        }
    }

    void Main()
    {
        clsMEF myMef = new clsMEF();
        MessageBox.Show(myMef.plugin.DoWork());
    }
}

Plugin

namespace MefPlugin
{
    [Export]
    public class Class1 : MefContracts.IPlugin
    {

        public String DoWork()
        {
            return "Plugin called";
        }
    }
}

Solution

  • After much playing and trial and error, I found the issue I was having was I had not added the current executing assembly (System.Reflection.Assembly.GetExecutingAssembly()) to the host's assembly catalogue along with the plugin's assemblies.

    Many thanks to @PanosRontogiannis who got me on the right lines - that answer worked brilliantly once the assembly was properly added.

    Here is the working code for others in need:

    Interface DLL

    using System.ComponentModel.Composition;
    
    namespace MefContracts
    {
        [InheritedExport]
        public interface IPlugin
        {
            String Work(String input);
        }
    
        [InheritedExport]
        public interface IHost
        {
            string Version { get; }
        }
    }
    

    Host Application

    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    
    namespace MyMEF
    {
    
        [Export]
        public class Class1 : MefContracts.IHost
        {
            public String Version
            {
                get { return "v1.00"; }
            }
        }
    
        class clsMEF
        {
            private CompositionContainer _container;
    
            [Import(typeof(MefContracts.IPlugin))]
            public MefContracts.IPlugin plugin;
    
            public clsMEF()
            {
                Compose();
            }
    
            void Compose()
            {
                var catalog = new AggregateCatalog();
                catalog.Catalogs.Add(new DirectoryCatalog("..\\..\\Extensions"));
                catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly())); // <-- THIS WAS THE MISSING PIECE
                _container = new CompositionContainer(catalog);
                try
                {
                    this._container.ComposeParts(this);
                }
                catch (CompositionException compositionException)
                {
                    Console.WriteLine(compositionException.ToString());
                }
    
            }
        }
        static class Program
        {
            static void Main()
            {
                clsMEF myMef = new clsMEF();
                MessageBox.Show(myMef.plugin.Work("My Input"));
            }
        }
    }
    

    Plugin

    using System.ComponentModel.Composition;
    
    namespace MefPlugin
    {
        [Export]
        public class Class2 : MefContracts.IPlugin
        {
            [Import(typeof(MefContracts.IHost))]
            public MefContracts.IHost Host;
    
            public String Work(String input)
            {
                return "Plugin Called (Input: " + input + "). Host Application Version: " + input + Host.Version;
            }
        }
    }