Prism - Manage ModulesCatalog when module versions change

Topics: Prism v4 - Silverlight 4
Sep 21, 2011 at 4:52 PM

We use PRISM in our Silverlight application.


Our ModulesCatalog.xaml contains the modules that are to be loaded OnDemand. The ModuleType attribute contains also the version (1.0.0.0) of our modules. This all worked fine.
But now we have change the AssemblyInfo.cs for all modules and specified 1.0.* as the version number.
How do we deal with this change in our ModulesCatalog.xaml? I have tried to remove the version part from ModuleType, but that does not work.


One possible solution I am thinking of is this:
Implement code in post-build event for every module, where the new version number together with the module name, is stored in a shared file.
When the BootStrapper in the Shell module starts, it will read this file and construct a ModuleCatalog based on these data.
Question is how do we construct such a post-build event script?


The reason we are trying to dynamically change the version numbers for the modules is to resolve a caching of old Xaps at the browser. I do not know if this will even resolve the caching problem.
Does anyone have some input on this?

Developer
Sep 22, 2011 at 7:09 PM
Edited Sep 22, 2011 at 7:12 PM

Hi,

The approach that you suggest using post-build events seems to be a valid approach to achieve you scenario.

As a possibility, you could try to create a module catalog in your bootstrapper without needing to register your modules in a xaml file. The AddModule method of the ModuleCatalog (which can be used to register modules) has an overload that accepts a ModuleInfo as a parameter. This ModuleInfo class requires only the following information (which is the same that would be contained in a xaml file):

  • Module name: A string with the "short" name of the module. This can be set through the constructor or the ModuleName property. (e.g "MyModule")
  • Module type: A string with the "complete" name of the module. This can be set through the constructor or the ModuleType property. (e.g. "MyModule.MyModule, MyModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
  • Dependencies: A collection of strings with the names of the modules that this module depends on. This can be set through parameters in the constructor or through the DependsOn property. (e.g. "OtherModule")
  • Initialization mode: the initialization mode of the module. This has to be set through the InitializationMode property. (You need to use the InitializationMode enumeration)
  • Ref: A string with the name of the xap file of this module. This has to be set through the Ref property. (e.g: "MyModule.xap")

So, as a possible approach, you could use a post-build event in each module to store the aforementioned information in a file (or various files). Then, in your main module you could have a service that retrieve the information for the file (or files), create a ModuleInfo for each module and return a collection containing those ModuleInfos. Then, the bootstrapper could consume this service and register each ModuleInfo in the module catalog.

For more details you can check the ModuleInfo class in the Prism library (inside the Modularity folder of the Prism.Desktop project).

Based on my understanding, a post-build event is simply a .bat file containing command lines. So it may be possible to run an application that could accept as arguments the aforementioned information for the module. Then, that application would organize and store the information in one or more files.

The specifics of how to use build events is out of scope of the Prism guidance. However, you might find some useful information in the following link:

I hope you find this useful,

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

Oct 6, 2011 at 10:05 AM

Is there not a better solution than this?  I have the exact same issue in that locally on my dev box I hard code the version to 1.0.0.0.  But once I commit this to TFS the assembly versions are overriden by the TFS build process.  Which then means that my catalog is pointing to an assembly with the wrong version.

I have several modules which are placed into a xap file each and then downloaded on demand.  I was wondering if it would be possible to download the the xap file which then I assume loads these dlls into the app domain and then do some kind of search based on the information (without version) on the information contained within the module info on the app domain?

 

Thanks

Phil

Developer
Oct 6, 2011 at 8:21 PM
Edited Oct 7, 2011 at 2:24 PM

Hi Phil,

The module info only contains the metadata necessary to describe a module (inlcuding the URI of the XAP file containing the dlls necessary for the module, i.e. the Ref property, and the Module Type, among others, as explained by Damian). However, the ModuleInfo is not added to the AppDomain; the assemblies corresponding to your module are the ones that get added.

ModuleInfos describe modules whose assemblies might or might not be loaded at the moment the ModuleInfo is created. For example, if your module's assembly is inside the main xap of your application, it should be already added to the AppDomain at the moment you build the ModuleInfo that describes this module. But if your module is meant to be loaded on demand, the module's assemblies will not be loaded at the time the ModuleInfo is builded (they will be downloaded and subsequently loaded into the AppDomain at the moment you call the ModuleManager.LoadModule method to indicate that you wish to loadd that module).

With that in mind, it seems that it would be possible to create a module info based on the information you obtain from an assembly that is already loaded into the AppDomain. For example, you could obtain the assembly qualified name (which includes the version of the assembly containing your module's type) by doing something like this:

var assemblyQualifiedName = Type.GetType("MyNamespace.MyModule").AssemblyQualifiedName;

where MyNamespace.MyModule is the name of the type of the main class in your module (i.e. the one that implements the IModule interface).

To find more information on how to download a xap file and load the assemblies inside it into the app domain, you might find the XapModuleTypeLoader class (which you can find in the Modules folder inside the Prism.Silverlight project of the Prism Library).

I hope you find this helpful.

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

Nov 14, 2011 at 4:54 PM

Just letting you guys know...

We've got the same scenario that TFS gives us a build number and all associated modules have the same version number. We need a version number to make sure the last modules are loaded in the browser on a new deployment (cache) and we need to know what version we are working with.

We got rid of the ModuleCatalog and are assigning the modules with the correct version numbers in code. Get the current shell module version and prepare your module catalog with the same version number.

public static class AssemblyExtensions
    {
        public static Version ParseVersionNumber(Assembly assembly)
        {
            var assemblyName = new AssemblyName(assembly.FullName);
            return assemblyName.Version;
        }
    }


protected override IModuleCatalog CreateModuleCatalog()
        {
            var version = AssemblyExtensions.ParseVersionNumber(typeof(Bootstrapper).Assembly).ToString();

            var moduleCatalog = new ModuleCatalog();

            //<prism:ModuleInfo Ref="x.Main.xap" ModuleName="Main" ModuleType="x.Main.ModuleInit, x.Main, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null" />

            moduleCatalog.AddModule
            (
                new ModuleInfo
                {
                    InitializationMode = InitializationMode.WhenAvailable,
                    Ref = "x.Main.xap",
                    ModuleName = "Main",
                    ModuleType = String.Format("x.Main.ModuleInit, x.Main, Version={0}, Culture=neutral, PublicKeyToken=null", version)
                }
            );
return moduleCatalog;
        }

 

Developer
Nov 14, 2011 at 9:02 PM

Hi,

Thanks for sharing this with the rest of the community, as it might be useful for other users pursuing this scenario.

Regards,

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

Aug 8, 2012 at 10:03 PM

Hi all, I am having the same Issue and I don't know how to get the module version number.

Actually I am doing this (see the code), but is seems that the Bootstrapper assembly version is diferent from the modules version. How I can sync the modules and shell version?

 

var version = ParseVersionNumber(typeof(Bootstrapper).Assembly).ToString();

            var moduleCatalog = new ModuleCatalog();

            //<prism:ModuleInfo Ref="x.Main.xap" ModuleName="Main" ModuleType="x.Main.ModuleInit, x.Main, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null" />

            moduleCatalog.AddModule
            (
                new ModuleInfo
                {
                    InitializationMode = InitializationMode.WhenAvailable,
                    Ref = "Import.Module.xap",
                    ModuleName = "Import.Module",
                    ModuleType = String.Format("Import.Module.ModuleInit, Import.Module, Version={0}, Culture=neutral, PublicKeyToken=null", version)
                }

            );

return moduleCatalog

Developer
Aug 9, 2012 at 7:57 PM

Hi,

First of all, take into account that as far as I know Prism does not provide any mechanism to manage the versioning of an application. As it can be seen in the following section of the Prism documentation, this is one of the challenges that are not addressed in Prism:

On the other hand, if your issue is related to to a caching issue with the module's XAP files (that is, your application is not downloading modules and is using an old locally cached version instead,) you can find a possible workaround to force your application to always download the XAP files from the server in the following work item:

By using the aforementioned workaround you would not need to change the versions of your modules.

I hope this helps,

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

Aug 9, 2012 at 8:34 PM

Thanks for your help...

I have noticed that if I use the workaround you mentioned, the xap module will refresh every time. I don't know if that is the best way to resolve this issue.

I have used another workround found in the web. Here is what I did (I share the code to know if it is a correct way to resolve this, let me know any doubt or suggestion)

1) On the siverlight object in the aspx file I defined the source to get the latest xap's creation date

(http://www.codeproject.com/Articles/143414/Prevent-your-Silverlight-XAP-File-from-Caching-in)

<%
    string strSourceFile = @"ClientBin/MyApp.xap";
    string param;
    string xappath;
    DateTime xapCreationDate;
    if (System.Diagnostics.Debugger.IsAttached)
        param = "<param name=\"source\" value=\"" + strSourceFile + "\" />";
    else
    {
        xappath = HttpContext.Current.Server.MapPath(@"") + @"\" + strSourceFile;
        xapCreationDate = System.IO.File.GetLastWriteTime(xappath);
        param = "<param name=\"source\" value=\"" + strSourceFile + "?ignore="
                + xapCreationDate.ToString() + "\" />";
    }
    Response.Write(param);
%>

2) Inside the initParameters I sent the creation date for each module (the ones that are created inside the bootsrapper)
       <param name="initParams" value="UserName=<%=User.Identity.Name%>,<% 
    strSourceFile = @"ClientBin/Admin.Module.xap";
   
        xappath = HttpContext.Current.Server.MapPath(@"") + @"\" + strSourceFile;
        xapCreationDate = System.IO.File.GetLastWriteTime(xappath);
    
    Response.Write("AdminModuleVersion=" + xapCreationDate.ToString());
%>" />

3) Pass the init params to bootsrapper

 private void Application_Startup(object sender, StartupEventArgs e)
        {
            Bootstrapper bootstrapper = new Bootstrapper();
            bootstrapper.AdminID = e.InitParams["AdminModuleVersion"];
            bootstrapper.Run();
        }

4)Build the module using the xap's creation date

protected override IModuleCatalog CreateModuleCatalog()
        {

           
            var moduleCatalog = new ModuleCatalog();

            moduleCatalog.AddModule
       (
           new ModuleInfo
           {
               InitializationMode = InitializationMode.WhenAvailable,
               Ref = string.Format("Admin.Module.xap?ignore={0}", AdminID),
               ModuleName = "Admin.Module",
               ModuleType = String.Format("Admin.Module.ModuleInit, Admin.Module, Version=1.0.0.0")
           }

       );

            return moduleCatalog;

        }

 

Developer
Aug 9, 2012 at 9:35 PM

Hi,

Thanks for sharing your findings with the rest of the community, as they might be helpful for other users with the same problem.

Also, I have added this workaround in the previously mentioned work item.

Thanks,

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