SoFunction
Updated on 2025-03-07

C# manually imports dependency modules in MEF framework

For simple scenarios, importing a dependency module in MEF is very simple. As long as you use ImportAttribute to mark the dependant members, the MEF module will automatically find and create the module. But sometimes the modules we rely on are context-dependent. At this time, the automatic assembly of the MEF framework cannot meet our needs. Here is a Log plug-in from my previous article as an example:

    class HostModule
    {
        [Import]
        ILogger logger = null;

        public string Name { get; private set; }

        public HostModule(string name)
        {
             = name;
            Compose();

            ("hello world");
        }

        void Compose()
        {
            var catalog = new AssemblyCatalog(().Assembly);
            var container = new CompositionContainer(catalog);
            (this);
        }
    }

    interface ILogger
    {
        void LogMessage(string msg);
    }

    [Export(typeof(ILogger))]
    class ConsoleLogger : ILogger
    {
        public void LogMessage(string msg)
        {
            ( + ": " + msg);
        }
    }

Now I want to add the module name as a prefix to the Log information and change it to the following form:

    [Export(typeof(ILogger))]
    class ConsoleLogger : ILogger
    {
        public string ModuleName { get; private set; }

        public void LogMessage(string msg)
        {
            (">> " + ModuleName + " | " +  + ": " + msg);
        }
    }

Because the MEF framework does not know the relationship between the two, it cannot be done directly by marking the ModuleName attribute with the ImportAttribute. So, how should we pass in this ModuleName?

Import through constructor:

The most direct idea is one way, the main modifications are as follows:

  • Create a constructor in the plug-in module, the parameters are the dependency modules that need to be imported, and the constructor is marked with ImportingConstructorAttribute.

  • In the constructor, the ImportAttribute tag parameter

  • Pass parameters in assembly function using ComposeExportedValue function

The sample code is as follows:

    class HostModule
    {
        [Import]
        ILogger logger = null;

        public string Name { get; private set; }

        public HostModule(string name)
        {
             = name;
            Compose();

            ("hello world");
        }

        void Compose()
        {
            var catalog = new AssemblyCatalog(().Assembly);
            var container = new CompositionContainer(catalog);
            ("ModuleName", );
            (this);
        }
    }

    interface ILogger
    {
        void LogMessage(string msg);
    }

    [Export(typeof(ILogger))]
    class ConsoleLogger : ILogger
    {
        public string ModuleName { get; private set; }

        [ImportingConstructor]
        public ConsoleLogger([Import("ModuleName")] string moduleName)
        {
             = moduleName;
        }

        public void LogMessage(string msg)
        {
            (">> " + ModuleName + " | " +  + ": " + msg);
        }
    }

One of the biggest disadvantages of this method is that it is troublesome. The above example is fine, but if you want to import more parameters, it will be a bit troublesome. In addition, if a dependent module is added in the future, several places must be modified at the same time. It is not concentrated enough, easy to modify and leak, and it is not easy to troubleshoot errors.

Import in members

The import method in a member is as follows:

  • Export parameters with Export tag in Host

  • Import parameters in plug-in module

The modified code is as follows, I marked the modified place:

    class HostModule
    {
        [Import]
        ILogger logger = null;

        [Export("ModuleName")]
        public string Name { get; private set; }

        public HostModule(string name)
        {
             = name;
            Compose();

            ("hello world");
        }

        void Compose()
        {
            var catalog = new AssemblyCatalog(().Assembly);
            var container = new CompositionContainer(catalog);
            (this);
        }
    }

    interface ILogger
    {
        void LogMessage(string msg);
    }

    [Export(typeof(ILogger))]
    class ConsoleLogger : ILogger
    {
        [Import("ModuleName")]
        public string ModuleName { get; private set; }

        public void LogMessage(string msg)
        {
            (">> " + ModuleName + " | " +  + ": " + msg);
        }
    }

This method is less and more intuitive to change, more scalable, and is much easier to use.

Further lift restrictions

The previous method is very convenient, but there is a limitation: the functional module is created by the MEF framework when assembling. However, sometimes, functional modules cannot be created by the MEF framework (such as UI objects in WPF programs, or some more complex context-related objects), but how do we manually import dependent external modules at this time? The MEF framework itself also provides a relatively complete solution: when performing the assembly of the ComposeParts function, you can just pass the two objects into one and assemble them together.

    class HostModule
    {
        ILogger logger = new ConsoleLogger();

        [Export("ModuleName")]
        public string Name { get; private set; }

        public HostModule(string name)
        {
             = name;
            Compose();

            ("hello world");
        }

        void Compose()
        {
            var container = new CompositionContainer();
            (this, logger);
        }
    }

    interface ILogger
    {
        void LogMessage(string msg);
    }

    class ConsoleLogger : ILogger
    {
        [Import("ModuleName")]
        public string ModuleName { get; private set; }

        public void LogMessage(string msg)
        {
            (">> " + ModuleName + " | " +  + ": " + msg);
        }
    }

summary:Although the three methods introduced above seem to be quite different, they are ultimately just different assembly forms. As long as you master the assembly principles of MEF, you can easily assemble the modules we need to achieve loosely coupled, simplified and modular programs.

This is all about this article about manually importing dependent modules in C# in the MEF framework. I hope it will be helpful to everyone's learning and I hope everyone will support me more.