ModuleInfo version - does it matter?

Topics: Prism v4 - Silverlight 4
Sep 27, 2011 at 4:10 PM

Hi there,

I am attempting to introduce versioning to my PRISM-based application and I am struggling to understand what effect the "Version" property in my module catalog has - because no matter what value I set in there, and no matter what version I have set on my Module XAP assemblies, the modules are loaded without error. I would have expected my application to throw an error if I specified an invalid version.

I have my modules defined in a module catalog, like so:

    <Modularity:ModuleInfoGroup InitializationMode="WhenAvailable">
        <Modularity:ModuleInfo Ref="CBS_ResearchBrowser_AdvancedSearch.xap" ModuleName="AdvancedSearchImpl" ModuleType="CBS_ResearchBrowser_AdvancedSearch.AdvancedSearchImpl, CBS_ResearchBrowser_AdvancedSearch, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    ...

    ...         
    </Modularity:ModuleInfoGroup>  

 

In my bootstrapper, I read this catalog as follows and it works OK:

        protected override IModuleCatalog CreateModuleCatalog()
        {
            var catalog = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri("/CBS_ResearchBrowser;component/ModuleCatalog.xaml", UriKind.Relative));
            return catalog;
        }

 

In the assembly.cs of each of my modules, I have the Assembly version defined as "1.0.*" - so the version number changes/refreshes each time I do a new build. Therefore, I don't understand how my application is able to load my modules, when I have specified Version=1.0.0.0 - because no such version of the XAP files exist !

Am I missing something? Ideally I would like to be able to specify the appropriate version of each of my Modules in the Catalog XAML, which would allow me the flexibility to drop in and specify new versions of individual modules. Surely this is possible?

 

Thanks,

Joe.

 

 

 

 

 

    </Modularity:ModuleInfoGroup> 
Developer
Sep 27, 2011 at 9:42 PM

Hi,

As far as I know, this behavior you are describing could appear when using MEF.

Based on my understanding, MEF uses the ModuleName property to find and resolve modules without requiring the ModuleType property. Therefore, as the version is specified in the ModuleType property, MEF does not take the version into account when finding modules. On the other hand, when using Unity, the ModuleType property is required to obtain the modules, so if the version changes, Unity should not be able to find them.

If this is your scenario, you might find the following thread useful where a similar issue is addressed:

I hope you find this useful,

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

Sep 28, 2011 at 11:35 AM

Thanks Damian,

I think you are right - the thread you linked to looks just like the problem I am having.

The "solution" posted however, is a little sketchy on detail. It mentions that the following single line of code is placed into a Custom Module Initializer.

Type type = Type.GetType(moduleInfo.ModuleType, false, true);

My question is - where exactly do I put this code and what is a Custom Module Initializer? I assume this custom class would over-ride the MEF ModuleInitializer somehow? I don't have much custom code in my application really. My bootstrapper class extends MefBootstrapper and that's about it (code posted below). Where/how would I integrate a custom module initializer?



    public class ResearchBrowserBootStrapper : MefBootstrapper
    {

        protected override void InitializeShell()
        {
            base.InitializeShell();
            // set the visual root of the Silverlight application
            // to the Shell (application's main view)
            Application.Current.RootVisual = (UIElement)Shell;
        }



        protected override DependencyObject CreateShell()
        {
            return Container.GetExportedValue<Shell>();
        }

       
        protected override void ConfigureAggregateCatalog()
        {
            base.ConfigureAggregateCatalog();
            // Prism's AggregateCatalog is a catalog of all MEF composable parts within the application.
            // We add the parts corresponding to the current assembly to it
            AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(CommonModule).Assembly));
            AggregateCatalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
        }


        protected override IModuleCatalog CreateModuleCatalog()
        {
            var modules = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri("/CBS_ResearchBrowser;component/ModuleCatalog.xaml", UriKind.Relative));
            return modules;
        }
    }
}


Developer
Sep 28, 2011 at 3:24 PM

Hi,

The module initializer "Handles loading of a module based on a type", that is to say, it is responsible for obtaining the class that implements the IModule interface from your module's project, and calling the Initialize method.

In the case of MEF, since module initialization is done through a different mechanism, there is a MefModuleInitializer, which inherits from the ModuleInitializer class. Following the suggestion from Adrien in the aforementioned thread, you could place the line he mentioned inside the MefModuleInitializer.CreateModule method. If you wish to avoid modifying the Prism Library, you could create a class of your own that inherits from the MefModuleInitializer class, overrides the CreateModule method and performs the check mentioned by Adrien. As mentioned in the MefModuleInitializer class description,

"Exports the ModuleInitializer using the Managed Extensibility Framework (MEF).
This allows the MefBootstrapper to provide this class as a default implementation.
If another implementation is found, this export will not be used."

, you can decorate your custom class with the Export attribute, specifying the IModuleInitializer interface as the contract type, which should replace the default implementation.

The resulting code would look like this:

 [Export(typeof(IModuleInitializer))]
    public class MefModuleInitializerWithFix : MefModuleInitializer
    {
        [ImportingConstructor]
        public MefModuleInitializerWithFix(IServiceLocator serviceLocator, ILoggerFacade loggerFacade, DownloadedPartCatalogCollection downloadedPartCatalogs, AggregateCatalog aggregateCatalog)
            : base(serviceLocator, loggerFacade, downloadedPartCatalogs, aggregateCatalog) { }

        protected override Prism.Modularity.IModule CreateModule(Prism.Modularity.ModuleInfo moduleInfo)
        {
            Type type = null;

           
            type = Type.GetType(moduleInfo.ModuleType, false, true);

            if (type == null)
            {
                // Throw an exception in case the type is not found
            }
            return base.CreateModule(moduleInfo);
        }
    }

Please take into account that, although the second parameter in the Type.GetType method is set to false (which means that, in case the type is not found, no exception should be thrown), if you have a type conflict, an exception will be thrown as well. So, in order to cover this scenario as well, your code could look like this:

 [Export(typeof(IModuleInitializer))]
    public class MefModuleInitializerWithFix : MefModuleInitializer
    {
        [ImportingConstructor]
        public MefModuleInitializerWithFix(IServiceLocator serviceLocator, ILoggerFacade loggerFacade, DownloadedPartCatalogCollection downloadedPartCatalogs, AggregateCatalog aggregateCatalog)
            : base(serviceLocator, loggerFacade, downloadedPartCatalogs, aggregateCatalog) { }

        protected override Prism.Modularity.IModule CreateModule(Prism.Modularity.ModuleInfo moduleInfo)
        {
            Type type = null;
            try
            {
                type = Type.GetType(moduleInfo.ModuleType, false, true);
            }
            catch (FileNotFoundException ex)
            {
                // Throw an exception in case there is a type conflict
            }

            if (type == null)
            {
                // Throw an exception in case the type is not found
            }
            return base.CreateModule(moduleInfo);
        }
    }

I hope you find this helpful.

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

Sep 28, 2011 at 4:34 PM

Thanks so much Guido, that works for me perfectly!! I had already thought about extending the MefModuleInitializer class, but I couldn't work out in my head how to tell my application that it should use my class rather than the MEF version.

Now I can use my module Catalog to control the versions of my individual modules. However, now I wonder... if I have 2 versions of the same XAP file, I cannot put these 2 same-named XAP's in the same web-application directory, can I?

e.g. Suppose I upgrade my catalog to point to a newer version of my module, version 2.0.0.0:

<Modularity:ModuleInfo Ref="CBS_ResearchBrowser_AdvancedSearch.xap" ModuleName="AdvancedSearchImpl" ModuleType="CBS_ResearchBrowser_AdvancedSearch.AdvancedSearchImpl, CBS_ResearchBrowser_AdvancedSearch, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

Is it possible, I wonder, to maintain two separate copies of the module XAP (version 1 and version 2) and for my application to locate and load the appropriate version at runtime?

I'm guessing that this could be difficult, as the XAP's would need to be in different locations, right?

Developer
Sep 28, 2011 at 4:53 PM

Hi,

Yes, it seems that you won't be able to put the same assembly with different versions in the same directory, as the XAP packages would have the same file name. You could, however, define some custom logic in your module catalog to check in different places to search for the appropriate file and only load it into the catalog if it is the correct one.

The specific implementation details will depend on your scenario, but you might find it helpful to know that you can access the list of ModuleInfos (which are the objects that contain the necessary metadata to define a Prism module) from the ModuleCatalog instance stored in the bootstrapper, modify it according to your needs, and create a new ModuleCatalog passing the modified list as the source. You could override the ConfigureModuleCatalog method to achieve that. The resulting code might look like this:

protected override void ConfigureModuleCatalog()
        {
            base.ConfigureModuleCatalog();
            var originalModuleInfoList = this.ModuleCatalog.Modules;

            var newModuleList = ApplyModifications(originalModuleInfoList);

            this.ModuleCatalog = new ModuleCatalog(newModuleList);
        }

where the ApplyModifications method will contain your logic to define version changes (which will probably imply modifying the ModuleType and Ref properties of the affected ModuleInfo instances).

Another alternative would be to directly generate the initial ModuleInfo list in a custom manner from within the CreateModuleCatalog method, instead of storing the information in a Xaml file.

I hope you find this helpful.

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

Sep 28, 2011 at 5:01 PM

Thanks Guido,

That makes sense and is extremely helpful, I really appreciate your detailed responses.

Thanks again,

Joe.