No valid Export found for IModuleManager

Topics: Prism v4 - Silverlight 4
Jan 16, 2012 at 7:26 PM
Edited Jan 16, 2012 at 7:28 PM

Hi All!

I have a Prism/MEF/MVVM solution with several Modules, that can appear in Regions of a Shell module. The Modules communicate with each other via an EventAggregator, and that’s all ok.

One of the Modules is a MainMenu, and that publishes Events when a menu-item is clicked. Other Modules react to that via subscription, hence the TitleBarModule shows the name-property of the chosen menu-item. That’s the framework and it works. What I would like to add now, is a ModuleManager, that would also react to the Events from the MainMenuModule. The responsibility of the ModuleManager would be to load the requested Module.

 The code I use for the Modules is – following MVVM principles – structured as follows: every View has the responsibility to instantiate it’s ViewModel with an instruction like this:

     <UserControl.DataContext> 

        <vm:StartViewModel/>

    </UserControl.DataContext>

 The View has no other codebehind than its own IntializeComponent.  That fires up the ViewModel allright. All the ViewModels need the EventAggregator (and some more shared functionality, like e.g. the NotifyPropertyChanged) so I have a BaseViewModel, from which they all Inherit. Here’s the relevant part of that BaseViewModel:

 

Imports System.ComponentModel.Composition

Imports Microsoft.Practices.Prism.Modularity

 

Public MustInherit Class Base_ViewModel

    Implements INotifyPropertyChanged

     <Import()> Public Local_EventAggregator As Microsoft.Practices.Prism.Events.IEventAggregator

    <Import()> Public Local_ModuleManager As IModuleManager

 

 And this is how the ViewModel inherits and is instantiated:

 

Imports System.ComponentModel.Composition

Imports Microsoft.Practices.Prism.Modularity

 

 Public Class StartViewModel

    Inherits Base_ViewModel

 

     Public Sub New()

        If Not IsDesignTime Then

            CompositionInitializer.SatisfyImports(Me)

            SubscribeToEvents()

        End If

    End Sub

 

     Private Sub SubscribeToEvents ()

        Local_EventAggregator.GetEvent(Of MenuGeselecteerd_Event).Subscribe(AddressOf ReactOnMenuChoice, True)

    End Sub

 

     Private Sub ReactOnMenuChoice (ByVal MenuChoice As String)

        If Not MenuChoice = String.Empty Then

            Select Case MenuChoice

                Case "#####"

                    Local_ModuleManager.LoadModule("#####Init") ‘##### is replaced by concrete MenuNames of course!

                  …

                 …

                Case Else

            End Select

        End If

    End Sub

 

As soon as the SatisfyImports is hit, this is the resulting error, pointing to the XAML at the location that is quoted above in this email:

 

1) No valid exports were found that match the constraint '((exportDefinition.ContractName == "Microsoft.Practices.Prism.Modularity.IModuleManager") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "Microsoft.Practices.Prism.Modularity.IModuleManager".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected." & vbCrLf & "" & vbCrLf & "Resulting in: Cannot set import 'Shell.StartViewModel.Locale_ModuleManager (ContractName="Microsoft.Practices.Prism.Modularity.IModuleManager")' on part 'Shell.StartViewModel'." & vbCrLf & "Element: Shell.StartViewModel.Locale_ModuleManager (ContractName="Microsoft.Practices.Prism.Modularity.IModuleManager") -->  Shell.StartViewModel" & vbCrLf & "" & vbCrLf & "   bij System.ComponentModel.Composition.CompositionResult.ThrowOnErrors(AtomicComposition atomicComposition)" & vbCrLf & "   bij System.ComponentModel.Composition.Hosting.ComposablePartExportProvider.Compose(CompositionBatch batch)" & vbCrLf & "   bij System.ComponentModel.Composition.Hosting.CompositionContainer.Compose(CompositionBatch batch)" & vbCrLf & "   bij System.ComponentModel.Composition.CompositionInitializer.SatisfyImports(ComposablePart part)" & vbCrLf & "   bij System.ComponentModel.Composition.CompositionInitializer.SatisfyImports(Object attributedPart)" & vbCrLf & "   bij Shell.StartViewModel..ctor()" & vbCrLf & "   --- Einde van intern uitzonderingsstackpad ---" & vbCrLf & "   bij System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)" & vbCrLf & "   bij Shell.StartView.InitializeComponent()" & vbCrLf & "   bij Shell.StartView..ctor()}

 

I read from this that there were no valid Exports found in the IModuleManager??? What am I missing here, of do I need to refer to anything else/more???

 

When I leave out the import of the IModuleManager in the Base_ViewModel, everything works fine – except of course that I do not have a ModuleManager. Should the ModuleManager be instantiated differently?

I have also tried to instantiate the ViewModel from the View’s code behind, but that gives another strange effect: When I do that, the import of the EventAggregator and the ModuleManager has to move to the ViewModel, because it cannot be inherited like in all the other Modules. That gives a non-responsive EventAggregator (another instance???) so no reaction after subscription. And thus the ReactOnMenuChoice is never hit (while all other modules still perfectly react!)

 

There are some more variants I have been experimenting with, but they all come to the same or comparable – and disappointing - results.

I am anxious to get to know the truth behind this strange behavior of the IModuleManager.  

Peter Klein

Developer
Jan 17, 2012 at 4:30 PM

Hi Peter,

As explained in a blog post by Bob Brumfield, it seems that the CompositionInitializer class utilizes its own container, which is different from the container initialized by your bootstrapper. Based on my understanding, the ModuleManager should be registered in the container that is initialized in your bootstrapper and that is usually used by Prism, but it's not available in the container used by the CompositionInitializer; and thus, when trying to resolve the dependencies the exception you mentioned is raised.

You can find more information about this as well as a work around to force the CompositionInitializer class to use the same container than Prism in the aforementioned blog post:

Also, you might find the QuickStarts and Reference Implementations provided by prism useful, as they show different approaches of how to initialize view models and resolve their dependencies:

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Jan 17, 2012 at 8:54 PM

Hi Damian,

Thanks for your reply. I have tried to follow the suggestion as given in your first reference and added the relevant code to the bootstrapper:

Protected Overrides Function CreateContainer() As CompositionContainer
	Dim Local_Container = MyBase.CreateContainer()
	' Initialize the CompositionHost so we can use CompositionInitializer       
	CompositionHost.Initialize(Local_Container)
	Return Local_Container
End Function

I presume this will instantiate the "right" container, but adding this (and only this) to my code does not give the so much desired result...

The other documentation is (among other stuff) the standard ModularityWithMef example. The part I cannot get working in my project is the simple reference to the command: ModuleManager.LoadModule("ModuleName"). That won't work for me, and I have spent more than a few hours studying those examples, I can assure!!

Perhaps some other suggestion or source for further reading?

Regards,

Peter

Developer
Jan 18, 2012 at 7:21 PM

Hi Peter,

We tried to recreate the scenario you are describing and we could reproduce the issue you are mentioning. However, we also applied the code snippet provided in the aforementioned blog post and it seems to resolve this problem.

You can find a repro-sample application portraying this in my SkyDrive account with the following name:

This sample is a version of the Hello World QuickStart modified to use MEF which include a mocked version of the classes you posted above. When running the application you should see a Button that, when clicked, will show a MessageBox showing the ModuleManager and the EventAggregator.

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Jan 20, 2012 at 5:46 PM

Hi Damian,

Thanks for your answer and providing me with a working example of how to achieve (more or less) what I am looking for. From examinig the sourcecode you provide, I get the impression that it is solved by moving the call to the IModuleManager to another module than the "core" application where the Shell is started from.

Does this actually mean that IModuleManager should NOT be called from the Shell-application? Or is this just a feasable workaround? I have changed my solution into this variant, but would appreciate very much to hear if this is just temporary or permanent because of fixed (mis??)behaviour of the IModuleManager. Hope to hear....

Best regards,

Peter

Developer
Jan 20, 2012 at 6:16 PM

Hi Peter,

Based on my understanding, you should be able to call the ModuleManager from the "Shell" project without problems. As a possible way to check this, you could move the view and view models from the HelloWorldModule project to the HelloWorld.Silverlight project (the "Shell" project) and consume it from that project.

Based on this, I believe the problem you were experiencing is not related to the location of the view model which imports the IModuleManager.

Regards,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Jan 21, 2012 at 8:07 AM

Thanks Damian,

In the mean time I have been restructuring my solution a little bit, to avoid any objections the IModuleManager might have. I intruduced a "broker" module, connecting the "need" for a certain module to the "satisfier" if that need, using the IModuleManager in that broker module. That is a very flexible setup for my purposes and helps to keep the application architecture as clear as possible.

I will  - in spare (???) time - try what you suggested and experiment with moving around between modules of the IModuleManager.

But what keeps me puzzled is: what went wrong in my initial setup?

Best regards,

Peter