Dynamic Module Catalog?

Topics: Prism v2 - Silverlight 2
Feb 23, 2009 at 6:38 PM
I would like to generate a dynamic module catalog for each user in order to enforce some customization and security features of our application. For example, a user might be a "viewer" in which case they would only get the default modules. But another user might be an "admin" and would receive the admin modules on top of the default modules.

So my question is, couldn't I just make my GetModuleCatalog method in my Bootstrapper do something like this...

return ModuleCatalog.CreateFromXaml(new Uri("https://www.someurl.com/catalog/ModuleCatalog.xaml", UriKind.Absolute));
 
Of course, I should mention that I'm talking strictly about a Silverlight app that's already running in a secure context via traditional Silverlight/ASP.NET Membership-style security where the user has already logged in via a login page before launching the silverlight application.

Please let me know if anybody has any thoughts or ideas.

Thanks,
Chris

 

Mar 5, 2009 at 3:24 PM

Hi Chris,

 

You could create different ModuleCatalogs and load a different one based on the user’s role (though you should have a way to map a user's role to a ModuleCatalog url). The problem with this approach is that you would have to maintain several catalog files.

 

Another possible approach to avoid having multiple files, is having a single ModuleCatalog and setting all modules that are security enforced to load on demand. You would then have a service to query for all available modules for a specific role and would load dynamically all the on demand modules available for that role.

 

For more information about loading modules on demand you can check the following articles from the Prism-v2 documentation:

·         How to: Load Modules on Demand

·         Modularity QuickStarts for Silverlight

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman

Apr 20, 2009 at 8:01 PM
Hi Damian,

Like Chris, I'm also interested in a way to dynamically create the ModuleCatalog. The solution you offered in your response is not a good solution in our case.

We are creating a distributed web application. The webserver with the site runs on a server which is located at the customer's location. We have a windows service that checks for updates and downloads what we call modules (ascx controls, dll, images, etc). Each client can have different modules in their application. For instance: Customer A has Module1 and Module2 and Customer B has Module2, Module4 and Module99.
We would also like to set-up a Silverlight front-end for this app using the Composite Guidance (which is great, btw :) ).

A possible solution I'm looking for would be to have is a ModuleCatalog which can be retrieved using a webservice, handler, etc.

It's impossible for us to know how many, let alone what kind of, modules will be added to the application in the future. If we use the current method it means we have to update the main application including the ModuleCatalog for each module we add/change/remove. This is not the scenario we're after.
Is there a possible solution for this in the current guidance?

The only thing I can come up with now is to dynamically create a ModuleCatalog on the server, extract the XAP, replace the ModuleCatalog with the generated one, XAP it again and have the client download it.

I hope the explanation clarifies the situation. If you need any more info, please contact me by e-mail.

Kind regards,

Rob Houweling
Apr 20, 2009 at 8:32 PM
Rob,

Through a series of experiments, I was eventually able to get this working a few different ways:

1.) ASP-driven ModuleCatalog.xml file, like this

protected virtual IModuleCatalog GetModuleCatalog() {

string catalogLocation = "/Whatever;component/ModulesCatalog.xaml";

Modularity.ModuleCatalogBuilder builder = Modularity.ModuleCatalogBuilder.CreateFromXaml(new Uri(catalogLocation, UriKind.Relative));

IModuleCatalog catalog = builder.GetUserCatalog();

return catalog;

}

The only problem I had with this approach was that I had to roll my own ModuleCatalogBuilder, and it became a headache to try and keep my version synched up to the (at the time) rapidly changing drops coming from the PRISMv2 dev team. However, this was nice because I could keep all of my custom logic in ASP.NET (which generated the ModulesCatalog.xaml file on the fly) in one place (albeit a bit disconnected from the Silverlight project) - but this might be exactly what you need per your comments.

2) Web Service - I eventually settled on using a call to a web service to get a list of "roles"for the current user, which I then used to programmatically build the Catalog on the client-side. This works pretty well, and the only trick is that the async nature of the web service call creates the need to re-wire the bootstrapper a little bit.

Chris

Feb 16, 2011 at 4:57 AM
dschenkelman wrote:

Hi Chris,

 

You could create different ModuleCatalogs and load a different one based on the user’s role (though you should have a way to map a user's role to a ModuleCatalog url). The problem with this approach is that you would have to maintain several catalog files.

 

Another possible approach to avoid having multiple files, is having a single ModuleCatalog and setting all modules that are security enforced to load on demand. You would then have a service to query for all available modules for a specific role and would load dynamically all the on demand modules available for that role.

 

For more information about loading modules on demand you can check the following articles from the Prism-v2 documentation:

·         How to: Load Modules on Demand

·         Modularity QuickStarts for Silverlight

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman

 

 

 

I prefer your 2nd approach, but I checked "PrismV4_Modularity With Mef QuickStart" and didn't find a way to map "roles" with modules.

I think it's a common requirement but Prism didn't provide. Did I miss something? 

Could you give some sample code to implement a customized MefBootstrapper? Thank you in advance.

Developer
Feb 16, 2011 at 4:25 PM

Hi,

The service mentioned in Damain Schenkelman's answer would be responsible for mapping roles to modules. For example, the service could have a dictionary that would map a role to the module's name, and on authentication it could then iterate through the modules that correspond to a certain role and call the ModuleManager.LoadModule method, passing the module's name as a parameter. That would load the modules associated to a specific role.

I hope you find this helpful.

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

Feb 17, 2011 at 2:59 AM
GuidoMaliandi wrote:

Hi,

The service mentioned in Damain Schenkelman's answer would be responsible for mapping roles to modules. For example, the service could have a dictionary that would map a role to the module's name, and on authentication it could then iterate through the modules that correspond to a certain role and call the ModuleManager.LoadModule method, passing the module's name as a parameter. That would load the modules associated to a specific role.

I hope you find this helpful.

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

Your answer helps and I got idea:

    I must implement a service to query for all available modules for the specific roles that authentication service gives back.

    But one possible problem is, how to call that web service in Shell? and how to handle the results in async nature of the web service?

Thanks for your answer in advance!

Feb 17, 2011 at 10:21 AM
cwiederspan wrote:
Rob,

Through a series of experiments, I was eventually able to get this working a few different ways:

1.) ASP-driven ModuleCatalog.xml file, like this

protected virtual IModuleCatalog GetModuleCatalog() {

string catalogLocation = "/Whatever;component/ModulesCatalog.xaml";

Modularity.ModuleCatalogBuilder builder = Modularity.ModuleCatalogBuilder.CreateFromXaml(new Uri(catalogLocation, UriKind.Relative));

IModuleCatalog catalog = builder.GetUserCatalog();

return catalog;

}

The only problem I had with this approach was that I had to roll my own ModuleCatalogBuilder, and it became a headache to try and keep my version synched up to the (at the time) rapidly changing drops coming from the PRISMv2 dev team. However, this was nice because I could keep all of my custom logic in ASP.NET (which generated the ModulesCatalog.xaml file on the fly) in one place (albeit a bit disconnected from the Silverlight project) - but this might be exactly what you need per your comments.

2) Web Service - I eventually settled on using a call to a web service to get a list of "roles"for the current user, which I then used to programmatically build the Catalog on the client-side. This works pretty well, and the only trick is that the async nature of the web service call creates the need to re-wire the bootstrapper a little bit.

Chris

 

Hi, Chris

You mentioned :

2) Web Service - I eventually settled on using a call to a web service to get a list of "roles"for the current user, which I then used to programmatically build the Catalog on the client-side. This works pretty well, and the only trick is that the async nature of the web service call creates the need to re-wire the bootstrapper a little bit.

Could you share the source code of your customized bootstrapper ? Thanks.

Developer
Feb 17, 2011 at 2:16 PM
Edited Feb 17, 2011 at 2:17 PM

Hi,

The service could be included in the module (that is to say, not as a web service). Alternatively, you could check the following resources, which might give you some insight on how to consume WCF or web services in Silverlight:

I hope you find this helpful.

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

Mar 11, 2011 at 7:22 PM

The links here are off topic from the real issue and do not address the problem. 

If I have a login screen (in the Shell). 
And I only want modules I have access to be present in the ModuleCatalog (for the Bootloader to load) 
Then how can I authenticate and load a ModuleCatalog.xml (stream or file created on demand, through service or something) using PRISM.

You cannot run the Shell outside the Bootloader and if you run the Bootloader it loads the Module Catalog which does not have scope of who is logged in.  It's a Chicken and Egg scenario.  Who came first?

Developer
Mar 14, 2011 at 5:44 PM

Hi,

One possibility to achieve what you're mentioning would be to override the Run method of your Bootstrapper to avoid calling the call the CreateModuleCatalog and Initializemodules methods. That way, you could call them only when you have loaded your custom ModulesCatalog, thus solving the timing issue you're experiencing.

I'm attaching the contents of the Bootstrapper.Run method to illustrate this:

        public override void Run(bool runWithDefaultConfiguration)
        {
            this.Logger = this.CreateLogger();

            if (this.Logger == null)
            {
                throw new InvalidOperationException(Resources.NullLoggerFacadeException);
            }

            this.Logger.Log(Resources.LoggerWasCreatedSuccessfully, Category.Debug, Priority.Low);

            this.Logger.Log(Resources.CreatingModuleCatalog, Category.Debug, Priority.Low);
            this.ModuleCatalog = this.CreateModuleCatalog();
            if (this.ModuleCatalog == null)
            {
                throw new InvalidOperationException(Resources.NullModuleCatalogException);
            }

            this.Logger.Log(Resources.ConfiguringModuleCatalog, Category.Debug, Priority.Low);
            this.ConfigureModuleCatalog();

            this.Logger.Log(Resources.CreatingCatalogForMEF, Category.Debug, Priority.Low);
            this.AggregateCatalog = this.CreateAggregateCatalog();

            this.Logger.Log(Resources.ConfiguringCatalogForMEF, Category.Debug, Priority.Low);
            this.ConfigureAggregateCatalog();

            this.RegisterDefaultTypesIfMissing();

            this.Logger.Log(Resources.CreatingMefContainer, Category.Debug, Priority.Low);
            this.Container = this.CreateContainer();
            if (this.Container == null)
            {
                throw new InvalidOperationException(Resources.NullCompositionContainerException);
            }

            this.Logger.Log(Resources.ConfiguringMefContainer, Category.Debug, Priority.Low);
            this.ConfigureContainer();

            this.Logger.Log(Resources.ConfiguringServiceLocatorSingleton, Category.Debug, Priority.Low);
            this.ConfigureServiceLocator();

            this.Logger.Log(Resources.ConfiguringRegionAdapters, Category.Debug, Priority.Low);
            this.ConfigureRegionAdapterMappings();

            this.Logger.Log(Resources.ConfiguringDefaultRegionBehaviors, Category.Debug, Priority.Low);
            this.ConfigureDefaultRegionBehaviors();

            this.Logger.Log(Resources.RegisteringFrameworkExceptionTypes, Category.Debug, Priority.Low);
            this.RegisterFrameworkExceptionTypes();

            this.Logger.Log(Resources.CreatingShell, Category.Debug, Priority.Low);
            this.Shell = this.CreateShell();
            if (this.Shell != null)
            {
                this.Logger.Log(Resources.SettingTheRegionManager, Category.Debug, Priority.Low);
                RegionManager.SetRegionManager(this.Shell, this.Container.GetExportedValue<IRegionManager>());

                this.Logger.Log(Resources.UpdatingRegions, Category.Debug, Priority.Low);
                RegionManager.UpdateRegions();

                this.Logger.Log(Resources.InitializingShell, Category.Debug, Priority.Low);
                this.InitializeShell();
            }

            IEnumerable<Lazy<object, object>> exports = this.Container.GetExports(typeof(IModuleManager), null, null);
            if ((exports != null) && (exports.Count() > 0))
            {
                this.Logger.Log(Resources.InitializingModules, Category.Debug, Priority.Low);
                this.InitializeModules();
            }

            this.Logger.Log(Resources.BootstrapperSequenceCompleted, Category.Debug, Priority.Low);

Also, it should be worth noting that, as you can check in the Modularity QuickStarts, you can use more than one mechanism of module loading in the same Bootstrapper.

I hope you find this helpful.

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

Aug 12, 2011 at 5:08 AM

Hi

This helped me 

http://forums.silverlight.net/post/566102.aspx

regards

AK