Loading modules during runtime.

Topics: Prism v4 - WPF 4
May 31, 2011 at 11:00 AM
Edited May 31, 2011 at 12:57 PM

Hello.

I'm really new in Prism and how to create modular applications.

I'm using WPF with Prism 4.0 and MEF. In a view a user can select modules. Each module is in a separate DLL and all of them in the same directory. 

Each module also implements an interface which i export. With ImportMany i get an array of this interface in the shell.

When the user click on an OK-Button i want to load these modules. To do that, i create a new ModulCatalog and return this catalog with eventaggregation to my Shell.

Now, the shell should load these modules.

My problem is, i dont't get the modules loaded. I know i could set the InitializationMode of the modules onDemand and load all modules in the bootstrapper. But in this case i have to many values in the imported interface-array.

I only wan't to import the values of the loaded modules.

I have the following code so far.

 

[Export]
public partial class Shell : Window, IPartImportsSatisfiedNotification
{
[Import]
IModuleManager ModuleManager { get; set; }

[Import]
IModuleCatalog ModuleCatalog { get; set; }

//Holds some information of the loaded modules
[ImportMany(typeof(IViewNames), AllowRecomposition=true)]
IViewNames[] ViewNames { get; set; }

//Some other code....

private void InvokeNavigationView(ModuleSelectionReadyPayload _payload)
{
//The modules i want to load
IModuleCatalog catalog = _payload.ModuleConfigCatalog;

//Add each Modul to the ModuleCatalog created in the Bootstrapper
foreach (ModuleInfo item in catalog.Modules)
{
ModuleCatalog.AddModule(item);
}
//Load the Modules
ModuleManager.Run();
}
}

 

But ModuleManager.Run() throws an exception

There is currently no moduleTypeLoader in the ModuleManager that can retrieve the specified module.

I have no idea how to create such a moduletypeloader. Why the modulemanager doesn't use the typeloaders it already has?

Is there any possibility to get the modules loaded???

 

Thanks a lot

and sorry for my bad english.

May 31, 2011 at 5:33 PM

Hi,

Based on my understanding of your scenario, you could load on demand your modules from the shell with only the name of the module. You just need to decorate your modules with the appropriate attributes (setting InitializationMode to OnDemand). You shouldn’t call the ModuleManager’s Run method since this is done by the MefBootstrapper.

You can read the following chapters of Prism Documentation in order to fulfill your scenario accordingly:

You also might find handy the following QuickStarts provided with Prism:

I hope you find this information useful.

Thanks,

Miguel Bronzovic
http://blogs.southworks.net/mbronzovic

May 31, 2011 at 6:23 PM

Thanks for your answer.

I'm familar with the Prism Developer Guide and the Quickstarts. Perhaps i didn't describe my problem exactly enough.

Loading modules on startup works fine.
In my application user should be able to configure modules. Each module has it's own functionality and more than one view (one for the desktop application and one to configure the module). But not every module is needed. So the user can select the required modules. After selecting the user confirm his choice. Now the application should load the selected modules and creates for each module a button in the navigation-area. By clicking on one of the button the configuration-view should be shown in the main content region of the application. Loading modules and display views in regions is no problem, but rather getting a list of the selected modules and create a list with buttons injecting views.

Because i have no information how many modules will be added and the name of the views, i decided to implement an interface.

 

    public interface IViewNames
    {
        string ConfigViewName();
        string DesktopViewName();
        string ButtonName();
    }
Each module implement this interface and has an export attribute
[Export(typeof(IViewNames))]
[ModuleExport(typeof(MyModule), InitializationMode = InitializationMode.OnDemand)]
public class MyModule : IModule, IViewNames
{
     //Some code
}

In the shell i import this with import many. So i can iterate throug the array and create a button.
But how can i say to my application that i only want to import the selected modules?

 

 

Developer
May 31, 2011 at 6:52 PM
Edited May 31, 2011 at 6:53 PM

Hi,

In order to achieve the scenario you're mentioning, you could create a view in your shell in which the user could select which modules to load, and when the selection is done, you could indicate the ModuleManager to load the selected modules, via the ModuleManager.LoadModules method.

Then, in order to show the buttons based on the modules that are loaded, you could have two approaches: you could add the button as a view from the module itself into a region, or you could subscribe to the ModuleManager.LoadModuleCompleted event and create a button in the handler for that event.

Note that the aforementioned event indicates that a module has been loaded. In order load certain modules on demand, you should set their InitializationMode to OnDemand, and call the ModuleManager.LoadModules method passing the name of the module, for each of the modules that you wish to load. Therefore, you shouldn't modify the ModuleCatalog, nor call the ModuleManager.Run method. It's worth mentioning that, even if you decorate a module with the ModuleExport attribute, if its InitializationMode is set to OnDemand, it won't be loaded until you call the ModuleManager.LoadModules method.

Also, note that, as one of the goals of modularity is to have loosely coupled components, you shouldn't import them directly in your shell.

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

May 31, 2011 at 7:27 PM
Edited May 31, 2011 at 7:51 PM

I tried to load the selected Modules via the ModuleManager.LoadModule method. But it throws an exception: There is currently no moduleTypeLoader in the ModuleManager that can retrieve the specified module.

If i understand the ModuleManager correctly, it only loads  Modules if they are already in the ModuleCatalog. Right? (This stands in the msdn library: Initializes the module on the ModuleCatalog with the name moduleName.)

So i have to put the Modules in the ModuleCatalog. This requiries a ModuleInfo. Right?

So how can i retrieve the necessary information when i only have the name of the DLL-File?

Developer
Jun 1, 2011 at 2:07 PM

Hi Steve,

The ModuleCatalog holds references to ModuleInfo instances that provide the necessary information about a module. There are many ways, however, of adding ModuleInfo instances to the ModuleCatalog. For example, if you use a DirectoryModuleCatalog, Prism automatically populates the ModuleCatalog with ModuleInfo instances based on the dll files present in a specified directory (more information here). You can also use a configuration file, providing the assembly file and the module type (as described here), or add them in code, if you have a reference to your modules from the shell (as described here).

Take into account that regardless of the mechanism you use to populate the ModuleCatalog, you can specify that your modules will be loaded on demand, thus enabling you to use the ModuleManager to decide when those modules are to be loaded, only passing the Module's name to it.

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

Jun 1, 2011 at 10:28 PM
GuidoMaliandi wrote:

Take into account that regardless of the mechanism you use to populate the ModuleCatalog, you can specify that your modules will be loaded on demand, thus enabling you to use the ModuleManager to decide when those modules are to be loaded, only passing the Module's name to it.

Ok. I have create the ModuleCatalog via Directory and initialize all Modules OnDemand at first. All modules have an Export [Export(typeof(IClass))]. In the Shell i import this via [ImportMany(typof(IClass))]. The problem is, that i only want to import the IClass from the modules which are intitialized. But the ImportMany in the Shell returns all IClasses regardless of the state of the module. Is this the normal approach of Prism or did i something wrong?

Developer
Jun 2, 2011 at 8:31 PM

Hi Steve,

It would be helpful if you could provide us with a repro sample, so that we can help you dig further into your scenario.

Thanks,

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

Jun 6, 2011 at 7:44 PM

Thank you for your suggestions Guido.

I think i have found a solution that solves my problem.

I will load Modules via AggregateCatalog.Catalogs.Add(new AssemblyCatalog(@"MyModule.dll")). So i can load Modules during runtime and get the Imports from the initialized Modules only.