moduleManager.loadModule("...") bug

Dec 9, 2008 at 11:57 PM

I updated from v6 to v7 and now I can't seem to load modules from the initialize() method of another module. You get stack overflow error.


I changed the Dynamic Module Loading example to reproduce a similar problem.

What I am trying to do is to have a common.module load ondemand and then retrieve other modules based on user profile. I was doing it in the initialize() method but that does not seem to work anymore. Is there an alternative way?


Thank you,


Pavel


 public class ModuleY : IModule

    {

        private readonly RegionManager regionManager;

        private readonly IModuleManager moduleManager;

        public ModuleY(RegionManager regionManager, IModuleManager moduleManager)

        {

            this.regionManager = regionManager;

            this.moduleManager = moduleManager;

        }

        public void Initialize()

        {

            moduleManager.LoadModule("ModuleX");

            //var viewY = new DefaultViewY(this.moduleManager);

            //this.regionManager["MainRegion"].Items.Add(viewY);

        }

    }

Dec 11, 2008 at 12:13 PM

Hi Pavel,

 

This is not the expected behavior for this situation. The team developing Prism-v2 has already been notified about this so they can fix it.

 

Loading ModuleX in the Initialize method of ModuleY provokes an infinite loop. The cause of the exception is:

1.       ModuleY is initialized it tries to load ModuleX.

2.       The RetrieveModules method of the ModuleManager begins retrieving ModuleX asynchronically.

3.       The LoadModulesThatAreReadyForLoad method is called.

4.       This method loads ModuleY, which calls Initialize. Back to step 1.

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman
Jun 19, 2009 at 7:25 AM

Hi Damian Schenkelman,

  I have a login module and admin module.. I have set the admin module as load on demand in boot strapper as below. Reason : I intend to replace LoginView with AdminView in the MainRegion after Authentication.

namespace App
{
    public class Bootstrapper :UnityBootstrapper
    {

        protected override DependencyObject CreateShell()
        {
            Shell shell = this.Container.Resolve<Shell>();
#if SILVERLIGHT
            Application.Current.RootVisual = shell;
#else
            shell.Show();
#endif
            return shell;
        }

        protected override IModuleCatalog GetModuleCatalog()
        {
            ModuleCatalog catalog = new ModuleCatalog();
            catalog.AddModule(typeof(LoginModule));
            catalog.AddModule(typeof(AdminModule),InitializationMode.OnDemand);
            return catalog;

        }

}

In the Login Module, I have a view called LoginView and in the cs file I have following code

 

namespace App.Modules.Login.Views
{
    public partial class LoginView : UserControl
    {
        private readonly IModuleManager moduleManager;
        public LoginView()
        {
            this.InitializeComponent();            
        }

        public LoginView(IModuleManager moduleManager) 
            :this()           
        {
            this.moduleManager = moduleManager;
        }

        private void btnLogin_Click(object sender, RoutedEventArgs e)
        {
            moduleManager.LoadModule("AdminModule");
        }
   }
}

When I run the app in debug mode, the moduleManager is getting assigned the root moduleManager. However after the shell is loaded, on button click, I get 

Object reference not set to an instance of an object. when it reaches moduleManager.LoadModule("AdminModule");

Here is the Module structure

Modules.Login
  LoginModule.cs
     LoginView.xaml
     LoginView.xaml.cs

Modules.Admin
  AdminModule.cs
     AdminView.xaml
     AdminView.xaml.cs

Where I am going wrong? Do you suggest a better approach?

Pooran

Jun 19, 2009 at 7:25 PM

Hi Pooran,

 

I have tried to reproduce your error with no success. However, if the module you are trying to load is not in the moduleManager the thrown exception is: “Module [ModuleName] was not found in the catalog”.

 

One possibility for that exception being thrown in that line is that the moduleManager is null (I got that error if the moduleManager is null even if the module I was trying to load did not exist). If that is the case, probably Unity is not injecting the IModuleManager instance implementation into your view. (If you have access to the source code you probably will be able to check this be setting a break point there).

 

You should make sure, that Unity is resolving the view (either by calling container.Resolve<LoginView>(), via constructor or setter injection). If Unity is injecting the dependency, the line in this constructor method would be being called:
public LoginView(IModuleManager moduleManager)

            :this()          

        {

            this.moduleManager = moduleManager; //this line here

        }

 

If Unity is setting the moduleManager but you are still having this problem, please send some more information about your problem.

 

The following article provides links to the most common scenarios when developing with Unity:

·         Key Scenarios

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman

Jun 21, 2009 at 8:52 AM
Edited Jun 21, 2009 at 8:54 AM

Hi Damian Schenkelman,

     I tried it. Still the error persists.. I have created a skeleton project and have uploaded to http://cid-8f83691dd01b3d44.skydrive.live.com/self.aspx/Prism/prismapp.rar

     Thanks and regards,

     Pooran

Jun 22, 2009 at 8:09 PM

Hi Pooran,

 

Assuming the sample application you provided uses similar code to the one you use, I believe I have found the cause of the problem. In your login module, you are using the following code to add the view to the MainRegion:

public void Initialize()

       {

           this.regionManager.Regions["MainRegion"].Add(new LoginView());

       }   

In this case, the view is not being built by the UnityContainer, so no dependencies are injected into it (for example the ModuleManager). To build the view with the UnityContainer you could use the following code:

public class LoginModule :IModule  

    {

       private readonly IRegionManager regionManager;

       private readonly IUnityContainer container;

 

       public LoginModule(IRegionManager regionManager, IUnityContainer container)

       {

           this.regionManager = regionManager;

           this.container = container;

       }

 

       public void Initialize()

       {

           LoginView view = this.container.Resolve<LoginView>(); //this creates makes the View go through the container’s pipeline.

           this.regionManager.Regions["MainRegion"].Add(view);

       }

    }

 

Now, the ModuleManager instance in the View will not be null and you should be able to perform on demand module loading.

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman

Jun 23, 2009 at 6:27 AM

Thanks Damian Schenkelman,

   It worked :) Now I have another question.

   I think the module is loading.. but module is invisible.  I am unable to see it. After logging in, the admin view should appear and login view should disappear. I have to keep few variables like role id into some global variables so that I can access them at later stage.

   Pooran

Jun 23, 2009 at 6:47 AM
Edited Jun 23, 2009 at 9:06 AM

Hi Damian Schenkelman,

      In Silverlight and WPF LoginModule I just added these lines before loading the new module.. 

            this.Height = 0;
            this.Visibility = Visibility.Collapsed;
            moduleManager.LoadModule("AdminModule");

      Now, I am able to see the admin module. Is there a better way of doing this?

     Pooran

Jun 23, 2009 at 10:22 PM

Hi Pooran,

 

I do not know your application specifics, but if you are going to use a single region to show both views, and that region will only have one active view at a time you should use a SingleActiveRegion (ContentControl).This kind of region allows you to activate/deactivate a particular view, and does not allow to have more than one active view at the same time (if you activate a view, the view that was previously active is automatically deactivated).

 

On your scenario, when the view is added to the Region (when you load the AdminModule), you could activate that view in the region. The code would look something like this:

manager.Regions["MainRegion"].Activate(adminView);

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman

Jun 24, 2009 at 5:36 AM

Hi Damian Schenkelman,

    The app is a port of asp.net app. There is a login page.. that redirects to respective pages based on role the user carries. In login module, I use the complete container to show the UI for login. Once the login info is correct, I have to show to AdminView or UserView. Each of these views will in turn have different regions like ToolbarRegion, DisplayRegion, SearchRegion and Status Region. In effect, I am loading these views in MainRegion. 

   I have to hide the login to show the AdminView. In the Admin View, there will be a logout button. On click of it, the user should be shown the Login Page again.

  Can you please share a codesample for implementation of SingleActiveRegion, Activating and Deactivating the View

  For your refernce, I have uploaded the updated sample application at http://cid-8f83691dd01b3d44.skydrive.live.com/self.aspx/Prism/prismapp.V2.rar

  Thanks and regards,

  Pooran

 

Jun 25, 2009 at 6:28 PM

Hi Pooran,

 

When you attach a region to any container that inherits from ContentControl, a SingleActiveRegion is created an attach to it. You can read more about the different regions here:

·         How to: Add a Region

 

Therefore, to create a SingleActiveRegion you could use code similar to this:

<ContentControl cal:RegionManager.RegionName="MainRegion" />

 

The following is a simplified scenario on how to achieve your functionality

 

You should add the login view to that region. Once you load the Admin Module (after login has been performed), you can use code similar to the one below to add the AdminView to the Region and show it:

public class AdminModule :IModule  

            {

            private readonly IRegionManager regionManager;

            private readonly IUnityContainer container;

 

            public LoginModule(IRegionManager regionManager, IUnityContainer container)

            {

                 this.regionManager = regionManager;

                  this.container = container;

            }

 

            public void Initialize()

            {

               AdminView view = this.container.Resolve<AdminView>(); //this creates makes the View go through the container’s pipeline.

               this.regionManager.Regions["MainRegion"].Add(view); //add the view to the region

               this.regionManager.Regions["MainRegion"].Activate(view); //this will automatically deactivate the login view and show the admin view

            }

      }

 

As you want to show the LoginView again once the user logs out, you should publish and event through event aggregator, to notify the LoginModule that logout was performed and activate the LoginView.

You can read more about using events with event aggregator in the following articles from the Prism-v2 documentation:

·         How to: Create and Publish Events

·         How to: Subscribe and Unsubscribe to Events

 

Once you are notified that a logout has been performed in your login module, you should activate the LoginView. For this you can search the Region for the View, by name for example, and then activate it:

object view = this.regionManager.Regions["MainRegion"].Views.GetView("LoginView");

this.regionManager.Regions["MainRegion"].Activate(view);

 

Have in mind that to be able to get a view by its name, you first have to add it with a name. To do this, when you add the LoginView to the MainRegion you should use code like this:

               this.regionManager.Regions["MainRegion"].Add(view, "LoginView");

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman

Jun 26, 2009 at 2:22 AM

Awesome.. worked like charm :) Thanks a lot Damian Schenkelman :)