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).
[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)
{
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;
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