Click here to Skip to main content
15,888,113 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Good morning all,
I am struggeling with a strange problem using nested imports in MEF.

I try to keep it quite basic, so I do not annoy you people with the details ;)
Maybe somebody has experienced similar behavior, I just have no more clue where to look at.

I have a ModuleImporter class which exposes an IModuleImporter interface.

This ModuleImporter is imported by my main application which works fine.

I also have an ExternalModule class which exports an IExternalModule interface, nothing special.

The ModuleImporter imports many IExternalModule lazily.

If I create an instance of the ModuleImporter in a Test-Form it perfectly imports the discovered IExternalModules.

Now the problem is if the ModuleImporter is imported by the Main-Application OnImportsSatisfied of ExternalModule is called twice!
I checked the ComposeParts method, it only runs once.

The problem is that the below shown setter is also called twice. Frist with one discovered IExternalModule, secondly with zero discovered IExternalModule. That causes the first correct value (1 element) to be set to the second wrong value (0 elements).

C#
[ImportMany]
public Lazy<IExternalModule>[] ModuleStorage { get; private set; }


I have no idea which direction to go from here... I checked if inner/outter structure is correct, but couldn't find any problems. Funny is also that it only happens if ModuleImporter is imported. If it is constructed manually the result of IExternalModules is always correct (1).
That lead me to belive that it has something to do with the nesting... I repead myself ;)

I added a sample project to illustrate the problem:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace TestRange
{
    public partial class frmImporterTest : Form
    {
        public frmImporterTest()
        {
            InitializeComponent();
        }


        private void button1_Click(object sender, EventArgs e)
        {


            // in this block there is one call to OnImportsSatisfied of Importer
            // and two calls to OnImportsSatisfied of ExtMod (should be only one)
            Importers importers = new Importers();
            List<IImporter> importersList = importers.ImportersList;

            foreach (IImporter imp in importersList)
            {
                List<IExtMod> extMods = imp.ExtMods;
                int a = 1;
            }

            int b = 0;

            // in this block there is one call to OnImportsSatisfied of Importer
            // and only one calls to OnImportsSatisfied of ExtMod (should be only one)
            Importer imp2 = new Importer();
            List<IExtMod> extMods2 = imp2.ExtMods;

            int c = 0;
        }
    }

    public interface IImporter
    {
        List<IExtMod> ExtMods { get; }
    }

    public interface IExtMod
    {
        string Name { get; }
    }

    public class Importers : MarshalByRefObject, IPartImportsSatisfiedNotification
    {

        public bool Loaded { get; protected set; }

        public Importers()
        {
            ComposeImporters();
        }


        private Lazy<IImporter>[] _importerStorage;
        [ImportMany]
        protected Lazy<IImporter>[] ImporterStorage
        {
            get { return _importerStorage; }
            private set { _importerStorage = value; }
        }

        public List<IImporter> ImportersList
        {
            get
            {
                if(ImporterStorage == null)
                {
                    return new List<IImporter>();
                }

                return ImporterStorage.Select(s => s.Value).ToList();
            }
        }
        

        public void ComposeImporters()
        {

            AggregateCatalog catalog = new AggregateCatalog();
            AssemblyCatalog cat = new AssemblyCatalog(this.GetType().Assembly);
            catalog.Catalogs.Add(cat);

            CompositionContainer container = new CompositionContainer(catalog);

            try
            {
                container.ComposeParts(this); 
            }
            catch (ReflectionTypeLoadException typeLoadException)
            {
                StringBuilder sb = new StringBuilder();

                foreach (Exception o in typeLoadException.LoaderExceptions)
                {
                    sb.AppendLine(String.Format("{0}", o));
                }

                Console.WriteLine(sb.ToString());

            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Loaded = true;
        }

        #region Implementation of IPartImportsSatisfiedNotification


        public void OnImportsSatisfied()
        {
            int i = 0;
        }

        #endregion
    }


    [PartCreationPolicy(CreationPolicy.Shared)]
    [Export(typeof(IImporter))]
    public class Importer : MarshalByRefObject, IImporter, IPartImportsSatisfiedNotification
    {
        
        public bool Loaded { get; protected set; }

        public Importer()
        {
            ComposeExMods();
        }


        private Lazy<IExtMod>[] _exModStorage;
        [ImportMany]
        protected Lazy<IExtMod>[] ExModStorage
        {
            get { return _exModStorage; }
            private set { _exModStorage = value; }
        }

        

        public void ComposeExMods()
        {

            AggregateCatalog catalog = new AggregateCatalog();
            AssemblyCatalog cat = new AssemblyCatalog(this.GetType().Assembly);
            catalog.Catalogs.Add(cat);

            CompositionContainer container = new CompositionContainer(catalog);

            try
            {
                container.ComposeParts(this); 
            }
            catch (ReflectionTypeLoadException typeLoadException)
            {
                StringBuilder sb = new StringBuilder();

                foreach (Exception o in typeLoadException.LoaderExceptions)
                {
                    sb.AppendLine(String.Format("{0}", o));
                }

                Console.WriteLine(sb.ToString());

            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Loaded = true;
        }

        #region Implementation of IPartImportsSatisfiedNotification


        public void OnImportsSatisfied()
        {
            int i = 0;
        }

        #endregion

        #region Implementation of IImporter

        public List<IExtMod> ExtMods
        {
            get
            {
                if (ExModStorage == null)
                {
                    return new List<IExtMod>();
                }

                return ExModStorage.Select(s => s.Value).ToList();
            }
        }

        #endregion
    }

    [PartCreationPolicy(CreationPolicy.Shared)]
    [Export(typeof(IExtMod))]
    public class ExtMod : IExtMod
    {
        public ExtMod()
        {
        }

        #region Implementation of IExtMod

        public string Name
        {
            get { return "Fubar"; }
        }

        #endregion
    }

}








Any hint is kindly appreciated,
have a great day
Andy
Posted
Updated 12-Jun-16 6:40am
v3
Comments
babax 12-Jun-16 12:51pm    
Hi there,

I'm having a similar problem with nested imports, too. The only difference is I'm not using Lazy<t>. May I ask you what solution did you finally get at?

Thanks.
Patrice T 12-Jun-16 13:39pm    
This is not an ongoing discussion !
Ask your own question with details about your problem.

1 solution

Hi Andy,

Take the simple solution, just use a generic MEF importer & loader. I hope my article can help you:

The Simplest Way to use MEF Fully Lazy DLL Loading[^]

Best Regards,

Shai
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900