Problem with importing IModuleManager in a Module

Topics: Prism v4 - WPF 4
May 2, 2012 at 1:10 PM

I'm developing a PRISM-Module (MyModule) that should list all available Modules found in the ModuleCatalog and upon user request load the selected module by using a ModuleManager. 

I'm using a MefBootstrapper and MyModule has an [Import] on the IModuleManager-interface but I get an exception when MEF is trying to compose the ModuleManager. I've seen a similar issue here: http://compositewpf.codeplex.com/discussions/258960 and it indeed seems like an issue that the IModuleInitializer not has been composed when trying to get the IModuleManager from the container.

My Question is: How can I import the IModuleManager during composition without getting an exception?

I've made a simple project to test this and the Module that should should list all available modules looks like this:

 

	[ModuleExport(typeof(MyModule))]
	public class MyModule : IModule
	{
		[Import]
		public IModuleManager ModuleManager { get; set; }

		public void Initialize()
		{
			
		}
	}

 

and my Bootstrapper:

 

	public class Bootstrapper : MefBootstrapper
	{
		protected override void ConfigureAggregateCatalog()
		{
			base.ConfigureAggregateCatalog();
			AggregateCatalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
			AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(MyModule.MyModule).Assembly));
		}

		protected override DependencyObject CreateShell()
		{
			return ServiceLocator.Current.GetInstance<Shell>();
		}

		protected override void InitializeShell()
		{
			Application.Current.MainWindow = (Window)Shell;
			Application.Current.MainWindow.Show();
		}
	}

The exception looks like this:

Microsoft.Practices.Prism.Modularity.ModuleInitializeException was unhandled by user code
  Message=An exception occurred while initializing module 'MyModule'. 
    - The exception message was: GetExportedValue cannot be called before prerequisite import 'Microsoft.Practices.Prism.MefExtensions.Modularity.MefModuleManager..ctor (Parameter="moduleInitializer", ContractName="Microsoft.Practices.Prism.Modularity.IModuleInitializer")' has been set.
    Check the InnerException property of the exception for more information. If the exception occurred 
    while creating an object in a DI container, you can exception.GetRootException() to help locate the 
    root cause of the problem. 
  Source=Microsoft.Practices.Prism
  ModuleName=MyModule
  StackTrace:
       at Microsoft.Practices.Prism.Modularity.ModuleInitializer.HandleModuleInitializationError(ModuleInfo moduleInfo, String assemblyName, Exception exception) in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Modularity\ModuleInitializer.cs:line 111
       at Microsoft.Practices.Prism.Modularity.ModuleInitializer.Initialize(ModuleInfo moduleInfo) in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Modularity\ModuleInitializer.cs:line 70
       at Microsoft.Practices.Prism.Modularity.ModuleManager.InitializeModule(ModuleInfo moduleInfo) in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Modularity\ModuleManager.cs:line 322
       at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesThatAreReadyForLoad() in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Modularity\ModuleManager.cs:line 209
       at Microsoft.Practices.Prism.MefExtensions.Modularity.MefModuleManager.OnImportsSatisfied() in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism.MefExtensions\Modularity\MefModuleManager.cs:line 113
       at System.ComponentModel.Composition.ReflectionModel.ReflectionComposablePart.NotifyImportSatisfied()
  InnerException: System.InvalidOperationException
       Message=GetExportedValue cannot be called before prerequisite import 'Microsoft.Practices.Prism.MefExtensions.Modularity.MefModuleManager..ctor (Parameter="moduleInitializer", ContractName="Microsoft.Practices.Prism.Modularity.IModuleInitializer")' has been set.
       Source=System.ComponentModel.Composition
       StackTrace:
            at System.ComponentModel.Composition.ReflectionModel.ReflectionComposablePart.EnsureGettable()
            at System.ComponentModel.Composition.ReflectionModel.ReflectionComposablePart.GetExportedValue(ExportDefinition definition)
            at System.ComponentModel.Composition.Hosting.CompositionServices.GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
            at System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportedValue(ComposablePart part, ExportDefinition export, Boolean isSharedPart)
            at System.ComponentModel.Composition.Hosting.CatalogExportProvider.CatalogExport.GetExportedValueCore()
            at System.ComponentModel.Composition.Primitives.Export.get_Value()
            at System.ComponentModel.Composition.ReflectionModel.ImportingItem.Cast(Type type, Export export)
            at System.ComponentModel.Composition.ReflectionModel.ImportingItem.CastSingleExportToImportType(Type type, Export export)
            at System.ComponentModel.Composition.ReflectionModel.ImportingItem.CastExportsToSingleImportType(Export[] exports)
            at System.ComponentModel.Composition.ReflectionModel.ImportingItem.CastExportsToImportType(Export[] exports)
            at System.ComponentModel.Composition.ReflectionModel.ReflectionComposablePart.SetImport(ImportingItem item, Export[] exports)
            at System.ComponentModel.Composition.ReflectionModel.ReflectionComposablePart.SetImport(ImportDefinition definition, IEnumerable`1 exports)
            at System.ComponentModel.Composition.Hosting.ImportEngine.PartManager.TrySetImport(ImportDefinition import, IEnumerable`1 exports)
            at System.ComponentModel.Composition.Hosting.ImportEngine.TrySatisfyImportSubset(PartManager partManager, IEnumerable`1 imports, AtomicComposition atomicComposition)
            at System.ComponentModel.Composition.Hosting.ImportEngine.TrySatisfyImportsStateMachine(PartManager partManager, ComposablePart part)
            at System.ComponentModel.Composition.Hosting.ImportEngine.TrySatisfyImports(PartManager partManager, ComposablePart part, Boolean shouldTrackImports)
            at System.ComponentModel.Composition.Hosting.ImportEngine.SatisfyImports(ComposablePart part)
            at System.ComponentModel.Composition.Hosting.CompositionServices.GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
            at System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportedValue(ComposablePart part, ExportDefinition export, Boolean isSharedPart)
            at System.ComponentModel.Composition.Hosting.CatalogExportProvider.CatalogExport.GetExportedValueCore()
            at System.ComponentModel.Composition.Primitives.Export.get_Value()
            at System.ComponentModel.Composition.ExportServices.GetExportedValueFromLazy[T](Export export)
            at System.ComponentModel.Composition.ExportServices.<>c__DisplayClass4`2.<CreateStronglyTypedExportOfTM>b__1()
            at System.Lazy`1.CreateValue()
            at System.Lazy`1.LazyInitValue()
            at System.Lazy`1.get_Value()
            at Microsoft.Practices.Prism.MefExtensions.Modularity.MefModuleInitializer.CreateModule(ModuleInfo moduleInfo) in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism.MefExtensions\Modularity\MefModuleInitializer.cs:line 117
            at Microsoft.Practices.Prism.Modularity.ModuleInitializer.Initialize(ModuleInfo moduleInfo) in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism\Modularity\ModuleInitializer.cs:line 65
       InnerException: 

Developer
May 3, 2012 at 6:51 PM

Hi,

We could verify this behavior and as mentioned before, this seems to be caused by a timing issue when composing the IModuleInitializer composable part.

So far, we found that this exception seems to appear only with MEF and when registering modules in Code through the AggregateCatalog like in your case. For example when we registered the module using a Configuration File we didn't experience this behavior.

Also no error appears when importing the IModuleManager instance, e.g. from the Shell Window (as shown in the ModularityWithMef QuickStart) or in a view model.

Additionally, we didn't encounter this error if the modules' InitializationMode was set to OnDemand and it was loaded after the application was initialized.

Similar to this approach we found that when adding the module's assembly after the bootstrapper process (using the AggregateCatalog class and InitializationMode set to WhenAvailable) the error was not thrown.

Also we created a work item in the issue tracker reporting this behavior, so that the team is aware of it.

Regards,

Agustin Adami
http://blogs.southworks.net/aadami

May 4, 2012 at 10:46 AM

Thanks for your answer aadami, and thanks for adding this as an issue. :)

Now this issue is pretty easy to work around (lazy loading of the IModuleManager for example) but I wanted to verify that I didn't do anything wrong on my side.

Thanks!