Drop 10 introduces an error loading a Module

Topics: Prism v4 - Silverlight 4
Oct 26, 2010 at 5:07 PM

I have an app I was running Drop 9 in and I updated the assemblies to Drop 10.

As soon as I did I started getting a runtime error indicating it couldn't find a module and it would throw an exception.  The extremely odd thing is that the module would load.

This is a large app and I can't post it anywhere.  The architecture is close that that of what's in this sample template I'm working on.   Note this sample doesn't exhibit the problem.

http://cid-ff05a29e13af7bfd.office.live.com/self.aspx/.Public/ModuleTemplate.rar

I tried trouble shooting and I can't figure out what's causing it.  My module name is called TruckStat and I set a break point in the Initialize of it and as soon as it runs past the end of Initialize the exception is immediately thrown.  It is a silverlight exception.

I had to revert back to the assemblies in Drop 9 but wanted to put this on your radar.  Again, if there is any tracing I can enable or something else I can do to gather info to send to you please advise.

Oct 26, 2010 at 5:19 PM

Could you post a complete stack trace and the full exception message.  It'll help us determine what may be going on.

Thanks,

Oct 26, 2010 at 5:33 PM

Unable to locate the module with type 'EyeCue.Modules.AppSettings.AppSettingsModule, EyeCue.Modules.AppSettings, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' among the exported modules. Make sure the module name in the module catalog matches that specified on ModuleExportAttribute for the module type.
  
at Microsoft.Practices.Prism.Modularity.ModuleInitializer.HandleModuleInitializationError(ModuleInfo moduleInfo, String assemblyName, Exception exception)
   at Microsoft.Practices.Prism.Modularity.ModuleInitializer.Initialize(ModuleInfo moduleInfo)
   at Microsoft.Practices.Prism.Modularity.ModuleManager.InitializeModule(ModuleInfo moduleInfo)
   at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesThatAreReadyForLoad()
   at Microsoft.Practices.Prism.Modularity.ModuleManager.IModuleTypeLoader_LoadModuleCompleted(Object sender, LoadModuleCompletedEventArgs e)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.RaiseLoadModuleCompleted(ModuleInfo moduleInfo, Exception error)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.HandleDownloadCompleted(DeploymentCatalog deploymentCatalog, AsyncCompletedEventArgs e)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.DeploymentCatalog_DownloadCompleted(Object sender, AsyncCompletedEventArgs e)
   at System.ComponentModel.Composition.Hosting.DeploymentCatalog.OnDownloadCompleted(AsyncCompletedEventArgs e)
   at System.ComponentModel.Composition.Hosting.DeploymentCatalog.HandleOpenReadCompleted(Object sender, OpenReadCompletedEventArgs e)
   at System.Net.WebClient.OnOpenReadCompleted(OpenReadCompletedEventArgs e)
   at System.Net.WebClient.OpenReadOperationCompleted(Object arg)

I have two Modules, one is called AppSettingsModule and the other is called TruckStatsModule

In this case it threw on AppSetting, but again, once I step pass the handled exceptions, the Module is loaded and working.

Also, note I'm loading the modules like this.

 protected override IModuleCatalog CreateModuleCatalog()
        {
            IModuleCatalog _modCat = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri(ModuleCatalogUri, UriKind.Relative));
            return _modCat;

        }

Then in the ConfigureAggregateCatalog()

I loop through the modules and add them as DeploymentCatalogs such as follows.

foreach (var module in this.ModuleCatalog.Modules)
            {
                _aggregCat.Catalogs.Add(CreateCatalog(module.Ref));
            }

private DeploymentCatalog CreateCatalog(string uri)
        {
            var catalog = new DeploymentCatalog(uri);
            catalog.DownloadCompleted += (s, e) => DownloadCompleted(s, e);
            catalog.DownloadAsync();
            return catalog;
        }

        private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
            else
            {
                lock (_lock)
                {
                    _modulesToLoad--;
                }

                if (_modulesToLoad <= 0)
                {
                    CompositionHost.Initialize(this.AggregateCatalog);
                    base.InitializeModules();
                }
            }
        }

What's odd is there is no error in the DownloadedCompleted function above in the bootstrapper.

I go back to Drop 9 assemblies and I don't have any issues. 

Oct 26, 2010 at 6:12 PM

I'm trying to figure out what you are trying to do here, so please bear with a few questions to help me understand the intent of the above code.

It looks like you are using a xaml module catalog.  This is straight-forward and right out of the Modularity samples.

Then, in the ConfigureAggregateCatalog you explicitly kick off the module downloads, wait for them to complete, and explicitly add the returned modules to the aggregate catalog. 

If this is the case, you are duplicating things that are already handled by the MefXapModuleTypeLoader.  If all the types you want to register with the Aggregate catalog are in the XAPs which are defined in the module catalog, you should be able to let the built in mechanisms in the bootstrapper download the modules, and when they are downloaded, they will be added to the AggregateCatalog and initialized. 

I would try removing the for loop you added to ConfigureAggregateCatalog, and see if the modules are properly added to the aggregate catalog by the built in mechanism that handles this.. If you look at the Modularity With MEF for Silverlight QuickStart, you can see that Modules B, E, & F are all in other XAPs, and they are only configured in the ModuleCatalog.XAML file.  Other than the module catalog being loaded by the bootstrapper, the bootstrapper has no knowledge of these, but the types still get registered in the container, and things still work.

If you are trying something that explicitly precludes this approach, let me know.

Also, keep in mind that between Drop 9 & 10 a few bugs were fixed in the XapModuleTypeLoader.  One of these fixes may have moved the responsibility for what you are trying to accomplish to after the ModuleLoaded event is fired as opposed to the DownloadComplete event, which could also be effecting things.

Thanks,
Mike

 

 

 

Oct 26, 2010 at 6:37 PM

Thanks for the prompt replies Mike.  I agree with what you're saying, I'm duplicating parts.  I actually have another thread on this.

http://compositewpf.codeplex.com/Thread/View.aspx?ThreadId=232387&ANCHOR#Post512874

If you look at the sample template I have shared out.  I'm doing pretty much the same thing but I only have one module in that sample.  Right now I'm working on a production application and this is causing me a big problem.

I was just about to start yet another thread but will try to tie it in here.  One thing I'm noticing is that ImportConstructor is pulling exports from a different container than CompositionInitializer.SatisfyImports.

In what we're doing with here, I have 2 modules that I load from a Xaml file.

Just making this call

protected override IModuleCatalog CreateModuleCatalog()
        {
            IModuleCatalog _modCat = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri(ModuleCatalogUri, UriKind.Relative));
            return _modCat;

        }

 is not updating the AggregateCatalog with the exports in the modules which are compiled in their own .xap.

In ConfigureAggregateCatalog() I have to add these two assemblies because there are exports I need in the base application such as Shell.xaml.

this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(GlobalValues).Assembly));

However, if I don't create a new AggregrateCatalog and populate it with the items above, then in my CreateShell where I do the following,

return this.Container.GetExportedValue<Shell>();

Also, if I don't do this in the ConfigureAggregateCatalog() function, none of my exports are available in my modules

foreach (var module in this.ModuleCatalog.Modules)
            {
                _aggregCat.Catalogs.Add(CreateCatalog(module.Ref));
            }

Let's go back to TruckStatModule, it has a TruckController which is Exported and in my TruckStatModule I have an Import for it so when the TruckStatModule loads, the constructor for TruckController fires.

TruckController uses the ImportConstructor to pull in approx 6 exported values.  Again, if I don't do the foreach above to load the modules into a new AggregateCatalog as DeploymentCatalogs, I get MEF exceptions that it can't find contracts for any of the Imports in the TruckController constructor.

However, doing it this way has presented yet another problem.  I have a View that loads with the TruckController and it has to use a ViewModelLocator.

The ViewModelLocator when it goes to return a ViewModel, it checks if it is null, it makes a call to CompositionInitializer.SatisfyImports(this);

However, what I'm finding is it's getting a separate ViewModel than the one that was imported from the ImportConstructor in TruckController so I've got to come up with something different.  I was hoping this was just a bug as I thought I would try Drop 10 out.  However, that has just added another problem to the mix.  I wanted to post it so you guys are aware but I have to go back to Drop 9 and figure out my original problem as I've spent way too much time on this.

In closing, all I want to do is be able to add modules which will be in the form of new Silverlight Application projects and most of the time will have their own .xap. 

I want to use the CreateModuleCatalog() and call Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(....) to load the modules.

When they are loaded, I would love to have everything in a single container so ImportingConstructor is pulling from the same container as CompositionInitializer.SatisfyImports

But that is just not happening.  (It could be something I'm not doing but the samples and docs are sparse so I'm piecing through as best I can.)

One other thing to note, instead of using a second AggregateCatalog, in the bootstrapper I've tried adding the deployment catalogs for the loaded modules into this.AggregateCatalog.Catalogs(CreateCatalog(module.Ref))

However, that causes the ImportConstructor for the controllers in my modules to fail with the exception that the Parts have changed and I don't have a way to set them to allowRecomposition=true.  (I've read a hack from Glen but I don't care for it.)

I hope that clarifies things more.  If not please let me know.

Thanks.

Oct 26, 2010 at 6:46 PM

Mike,

I'll add, I'm creating the Template I have linked above to share out with the world.  If it's easier to work off it, take a look at it and we can work off it to try and get the catalog stuff right.  WIth that it may be better to track how to get the parts into the single container or how to make sure SatisfyImports() pulls from the same container as ImportingConstructor inside a module.

Even though we get that working, this I created this thread for still exists and it may be better to keep the two separate.

Anyway, the sample, I've spent a lot of time trying to integrate Prism 4 into an existing app I need to make modular.  The sample is going to be a base solution I can use for future projects when I get it finished.

In addition to dynamically loading modules, I'm trying to get a region adapter for a toolbar control in it to work.

When I'm finished, I'd be glad to share it with you guys to post.  (I'll post it on my site either way when it's finished.)

 

Oct 26, 2010 at 7:18 PM

I've got to get this work so I'll post as I go along.  I went back and changed ConfigureAggregateCatalog() removing the foreach as you suggested.

It now looks like:

protected override void ConfigureAggregateCatalog()
        {
            base.ConfigureAggregateCatalog();

            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(GlobalValues).Assembly));            

            CompositionHost.Initialize(this.AggregateCatalog); 
            
        }
We'll just focus on the TruckStatModule here.  As mentioned above, this module has a TruckController class which I use to intialize and coordinate everything in this module.  The code above,
satisfies it's [ImportConstructor] imports just fine.
However, if I don't add the CompositionHost.Initialize(this.AggregateCatalog), the CompositionInitializer.SatisfyImports(this); fails indicating it can't find a contract for it's import.  That 
shouldn't be because it worked just fine in the [ImportingConstructor] of TruckController.
When I add, CompositionHost.Initialize(this.AggregateCatalog); 
Now the CompositionInitializer.SatisfyImports(this); doesn't fail anymore, however it's pulling back a separate instance of a ViewModel that what the constructor of TruckController Imports.
I know this because all my ViewModels in the TruckStatModule, I set a TruckController property on them.
So in the ViewModelLocator, TruckController is null when it should have a value.  (I assign other values to ViewModels in the controller based on logic and all of those are alway null in the ViewModel
my ViewModelLocator for this module.
So I'm still left with why CompositionInitializer.SatisfyImports in my TruckStat module is getting a different ViewModel than what the ImportConstructor in the same module gets.  My ViewModels are 
created as shared.
Also, Drop 10 of the assemblies is still throwing the same exception that caused me to start this particular case.
So I have to go back to Drop 9.
Developer
Oct 26, 2010 at 9:10 PM

kfrosty,

Mike asked me to take a look at this to see if I could spot anything.  A couple things.

* You shouldn't need to enumerate or initialize your own modules, or even setup your own deployment catalogs.  As long as the MOduleINfo's in the Module catalog have an appropriate Uri (pointing to a xap) are set to the load 'WhenAvailable'.  The modulemanager should:
  - Download the xap using a DeploymentCatalog
  - Add the catalog to the AggregateCatalog
  - [Import] and initialize any IModule's in the catalog

* How are you getting views into regions?  Are these added by the IModule.Initialize code or by some other mechanism? 

I know you've spent a bunch of time on this, but do you have a small repro that we could take a look at?

Thanks,

-b

Developer
Oct 26, 2010 at 9:21 PM

Another question for clarification:

It seems like you have items in your modules on which your Shell is dependent, is this true?  T

ypically, we would expect the shell to be created and then modules could register or add views to a region when they're initialized.

-b

Oct 26, 2010 at 9:59 PM

Hello Brian,

No I have nothing in my main shell that relies on any modules. I have an IPopupController & ILogService and they are defined in my Common Assembly.

Here are the names of my assemblies for the app at work.

EyeCue - Main SL app that contains Shell. This also contains a ShellController class with the following constructor.

[ImportingConstructor]

public ShellController(IShellViewModel shellVieModel, ILogService logService, IPopupController popupController)

LogService & PopupController are defined in EyeCue.Common below.

EyeCue.Common - Contains things common to the app. This is why I do this this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(GlobalValues).Assembly));

Modules

EyeCue.Modules.AppSettings

EyeCue.Modules.TruckStats

In shell I have a ContentControl and I load a UserControl called RootTruckTemplate.xaml into it. It contains several regions. The main one being RootTabRegion.

When the TruckStatModule loads, it inserts into RootTabRegion.

From: brumfb [mailto:notifications@codeplex.com]
Sent: Tuesday, October 26, 2010 5:21 PM
To: kkfrost@hotmail.com
Subject: Re: Drop 10 introduces an error loading a Module [CompositeWPF:232387]

From: brumfb

Another question for clarification:

It seems like you have items in your modules on which your Shell is dependent, is this true? T

ypically, we would expect the shell to be created and then modules could register or add views to a region when they're initialized.

-b

Read the full discussion online.

To add a post to this discussion, reply to this email (CompositeWPF@discussions.codeplex.com)

To start a new discussion for this project, email CompositeWPF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com

Oct 26, 2010 at 10:07 PM

Brian,

I've put this link in. http://cid-ff05a29e13af7bfd.office.live.com/self.aspx/.Public/ModuleTemplate.rar

You can download the Trial Telerik controls if you need to compile it.

To get it to where I'm at right now, you can create a ViewModel and a view that needs to use a ViewModelLocator.

In the Module1Controller, set the controller value for the ViewModel and then set a break point in your ViewModelLocator or anywhere you make a call to CompositionInitializer.SatisfyImports(this);

You'll see that it pulls a ViewModel totally separate than what you get from an ImportConstructor.

As far as this module loading error even when the module loads, I have no idea of how to reconstruct a repro. I just know it happens and the app is too large to try and sort through and figure out a "small repro".

If you send me a private email address I can probably send you the code but not the database it runs against.

From: brumfb [mailto:notifications@codeplex.com]
Sent: Tuesday, October 26, 2010 5:10 PM
To: kkfrost@hotmail.com
Subject: Re: Drop 10 introduces an error loading a Module [CompositeWPF:232387]

From: brumfb

kfrosty,

Mike asked me to take a look at this to see if I could spot anything. A couple things.

* You shouldn't need to enumerate or initialize your own modules, or even setup your own deployment catalogs. As long as the MOduleINfo's in the Module catalog have an appropriate Uri (pointing to a xap) are set to the load 'WhenAvailable'. The modulemanager should:
- Download the xap using a DeploymentCatalog
- Add the catalog to the AggregateCatalog
- [Import] and initialize any IModule's in the catalog

* How are you getting views into regions? Are these added by the IModule.Initialize code or by some other mechanism?

I know you've spent a bunch of time on this, but do you have a small repro that we could take a look at?

Thanks,

-b

Read the full discussion online.

To add a post to this discussion, reply to this email (CompositeWPF@discussions.codeplex.com)

To start a new discussion for this project, email CompositeWPF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com

Oct 26, 2010 at 11:53 PM

Brian,

I'm digging around this evening trying to figure out why my CompostionInitializer.SatisfyImports is not importing the same Exports in a module as the ImportConstructor.

I'm reading this in the MEF documentation.

DeploymentCatalog

DeploymentCatalog downloads parts from XAPs living on the web server. Using it you can break your application into as many XAPs as you like and use it to bring them back together. DeploymentCatalog downloads catalogs asynchronously and implements the async event pattern in order to allow you to monitor the download i.e. track completion, failures, etc. Because DeploymentCatalog is recomposable hower it is not required for you to do this, but we recommend you do. To use DeploymentCatalog with CompositionInitializer you must override the default configuration using CompositionHost otherwise CompositionInitializer will never see the downloaded parts.

http://mef.codeplex.com/wikipage?title=DeploymentCatalog&referringTitle=OverridingHostConfig

So if you are building DeploymentCatalogs then how are they going to be available to the CompositionInitializer in a module?

I'm making the call to Initialize but again, it appears that CompositionInitializer is pulling from a different container than ImportingConstructor.

Developer
Oct 27, 2010 at 2:15 AM

We don't do anything to make them explicitly available to CompositionInitializer.  The MefBootstrapper is creating an AggregateCatalog that everything is added to when download.  The DeploymentCatalogs, then, are added to the aggregate catalog when download is completed. 

It appears for CompositionInitializer to the static container must be setup and it creates a new one if none is provided.  Can you inspect the global container used by CompositionInitializer.SatisfyImports() and compare it to the CompositeContainer created in the MefBootstrapper?  Since the bootstrapper isn't explicitly registering with the CompositionHost, you might try overriding the CreateContainer() to register the returned container with CompositionHost.Initialize().

Bob

 

 

 

Oct 27, 2010 at 2:22 AM

Thanks.  How do you go about looking at the Global Container SatisfyImports() pulls from? 

 

Oct 27, 2010 at 1:49 PM

Ok, in trying your suggestion, I came up with the bootstrapper code below. Everything is at a particular place to get the Container populated with everything needed. Quite frankly I have to question why I need to put all the module parts in the central container as I really don’t want each module having parts to other modules unless I specifically specify it.

At any rate, right now before I initialize the Shell, the this.Container contains all the Imports. However, with this code, even though this.ModuleCatalog.Modules contains my modules, on drop 9 assemblies, I’m getting the exception that it can’t load my modules. One thing to note base.InitializeModules();

Fails to run.

I get an exception that No Valid exports were found that match the constraint IModuleManager. So digging through Prism, I found.

IModuleManager manager = ServiceLocator.Current.GetInstance<IModuleManager>();

manager.Run();

manager contains the Modules that were populated from my xaml file but the modules never load. I know the file is right because if I go back to calling the internal CreateFromXaml, the modules load. I’m just back to have the problem with SatisfyImports() not being able to resolve parts. (Which is odd because I’m just trying to resolve parts that are Exported in the TruckStats.xap which is local.)

I have no idea if what I’ve done will solve the problem but for now I’m stuck with it not loading modules even though they are there.

Here’s the bootstrapper code. You’ll note I use _modulesToLoad to determine when all the modules have loaded and then I run InitializeShell and then try to load the modules.

public class Bootstrapper : MefBootstrapper

{

private const string ModuleCatalogUri = "/EyeCue;component/ModulesCatalog.xaml";

AggregateCatalog _aggregCat = new AggregateCatalog();

CompositionContainer _moduleCompositionContainer;

object _lock = new object();

volatile int _modulesToLoad = 0;

protected override void ConfigureAggregateCatalog()

{

this.ModuleCatalog = CreateFromXaml(new Uri(ModuleCatalogUri, UriKind.Relative));

_modulesToLoad = this.ModuleCatalog.Modules.Count();

foreach (var module in this.ModuleCatalog.Modules)

{

_aggregCat.Catalogs.Add(CreateCatalog(module.Ref));

}

}

protected override void ConfigureContainer()

{

this.Container.ComposeExportedValue<ILoggerFacade>(this.Logger);

this.Container.ComposeExportedValue<IModuleCatalog>(this.ModuleCatalog);

this.Container.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(this.Container));

//base.ConfigureContainer();

}

protected override IModuleCatalog CreateModuleCatalog()

{

// This just returns new ModuleCatalog()

return base.CreateModuleCatalog();

}

protected override DependencyObject CreateShell()

{

this.Container.ComposeExportedValue<AggregateCatalog>(_aggregCat);

this.Container = CompositionHost.Initialize(new DeploymentCatalog(), _aggregCat); //add the current XAP.

CompositionInitializer.SatisfyImports(this);

return this.Container.GetExportedValue<Shell>();

}

protected override void InitializeModules()

{

//base.InitializeModules();

}

protected override void InitializeShell()

{

// Only run when our modules are loaded.

if (_modulesToLoad <= 0)

{

#if SILVERLIGHT

App _app = (App)Application.Current;

_app.InitializeRootVisual((Shell)this.Shell);

#else

Application.Current.MainWindow = (Shell)this.Shell;

Application.Current.MainWindow.Show();

#endif

IShellController _shellController = this.Container.GetExportedValue<IShellController>();

_shellController.Initialize();

}

}

//[Import]

//public Shell Shell { get; set; }

//[Import(typeof(IShellController))]

//public IShellController ShellController { get; set; }

private DeploymentCatalog CreateCatalog(string uri)

{

var catalog = new DeploymentCatalog(uri);

catalog.DownloadCompleted += (s, e) => DownloadCompleted(s, e);

catalog.DownloadAsync();

return catalog;

}

private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)

{

if (e.Error != null)

{

AppCommands.RaiseErrorMessage.Send(new AppRaiseErrorEvent(e.Error, e.Error.Message));

}

else

{

lock (_lock)

{

_modulesToLoad--;

}

if (_modulesToLoad <= 0)

{

InitializeShell();

IModuleManager manager = ServiceLocator.Current.GetInstance<IModuleManager>();

manager.Run();

}

}

}

/// <summary>

/// Creates a <see cref="ModuleCatalog"/> from XAML.

/// </summary>

/// <param name="xamlStream"><see cref="Stream"/> that contains the XAML declaration of the catalog.</param>

/// <returns>An instance of <see cref="ModuleCatalog"/> built from the XAML.</returns>

private ModuleCatalog CreateFromXaml(Stream xamlStream)

{

if (xamlStream == null)

{

throw new ArgumentNullException("xamlStream");

}

#if SILVERLIGHT

string xaml;

using (System.IO.StreamReader reader = new System.IO.StreamReader(xamlStream))

{

xaml = reader.ReadToEnd();

}

return XamlReader.Load(xaml) as ModuleCatalog;

#else

return XamlReader.Load(xamlStream) as ModuleCatalog;

#endif

}

/// <summary>

/// Creates a <see cref="ModuleCatalog"/> from a XAML included as an Application Resource.

/// </summary>

/// <param name="builderResourceUri">Relative <see cref="Uri"/> that identifies the XAML included as an Application Resource.</param>

/// <returns>An instance of <see cref="ModuleCatalog"/> build from the XAML.</returns>

private ModuleCatalog CreateFromXaml(Uri builderResourceUri)

{

var streamInfo = System.Windows.Application.GetResourceStream(builderResourceUri);

if ((streamInfo != null) && (streamInfo.Stream != null))

{

return CreateFromXaml(streamInfo.Stream);

}

return null;

}

}

Developer
Oct 27, 2010 at 4:24 PM

There are a few things I see in the bootstrapper that I wouldn't expect to need to be done here, such as setting up a DeploymentCatalog.  You should just be able to specify the contents of the ModuleCatalog and Prism will take care of downloading and initializing your modules.

If you haven't seen the ModularityWithMef Quickstart I would encourage you to take a look at that.  It actually shows a number of ways modularity can be accomplished, but the way that Modules B, E, and F are registered in this quickstart should be close to what you're trying to accomplish.  These are specified in the ModulesCatalog.xaml and are downloaded internally using a DeploymentCatalog.

The bootstrapper also shouldn't need to register services in a container that are registered by the base MefBootstrapper, such as the IModuleCatalog or IServiceLocator.

We also have tended to use the CompositionInitializer but instead resolve the view (or view model) from the container via IServiceLocator, if needed.  Let me put together a quick sample that uses CompositionInitializer and show you how I can see this all fitting together, which will hopefully help.

 

Oct 27, 2010 at 4:48 PM

Thanks.

I’m not partial to CompositionInitializer. I can try the IServiceLocator to get a container avenue but do you have something similar to how I could get an instance of it in something like a ViewModelLocator?

As for the DeploymentCatalog, if I don’t use it in the Initialize things don’t work. Plus I got that from the MEF documents which state to do that as it’s the only way things will be available a separate .xap file.

Yes, I’ve looked at the ModularityWithMEF quite extensively. The only problem is there are no views or viewmodels in it that really do anything a real app would do.

I’m going to put a controller, viewModel and view in moduleE and see if I can get you a repro.

From: brumfb [mailto:notifications@codeplex.com]
Sent: Wednesday, October 27, 2010 12:24 PM
To: kkfrost@hotmail.com
Subject: Re: Drop 10 introduces an error loading a Module [CompositeWPF:232387]

From: brumfb

There are a few things I see in the bootstrapper that I wouldn't expect to need to be done here, such as setting up a DeploymentCatalog. You should just be able to specify the contents of the ModuleCatalog and Prism will take care of downloading and initializing your modules.

If you haven't seen the ModularityWithMef Quickstart I would encourage you to take a look at that. It actually shows a number of ways modularity can be accomplished, but the way that Modules B, E, and F are registered in this quickstart should be close to what you're trying to accomplish. These are specified in the ModulesCatalog.xaml and are downloaded internally using a DeploymentCatalog.

The bootstrapper also shouldn't need to register services in a container that are registered by the base MefBootstrapper, such as the IModuleCatalog or IServiceLocator.

We also have tended to use the CompositionInitializer but instead resolve the view (or view model) from the container via IServiceLocator, if needed. Let me put together a quick sample that uses CompositionInitializer and show you how I can see this all fitting together, which will hopefully help.

Read the full discussion online.

To add a post to this discussion, reply to this email (CompositeWPF@discussions.codeplex.com)

To start a new discussion for this project, email CompositeWPF@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com

Oct 27, 2010 at 5:18 PM

Ok, I've modified your ModularityWithMef and uploaded it to my skydrive.

http://cid-ff05a29e13af7bfd.office.live.com/self.aspx/.Public/ModularityWithMef.rar

Check out ModuleE.  This is not 100% exactly what my real app is like but it's close and gets you in the right direction.  I added a controller, a ViewModle and a View.

The controller imports the ViewModel and set's it's controller property.

Then I go to try and register the ModuleEView with a Region.  Inside the ModuleEView constructor I put a call to CompositionInitializer.SatisfyImports(this);

It fails indicating it can't find a contract for ModuleEViewModel but you see the ImportConstructor for ModuleEController resolved it just fine.

Ok, this gets to where I was a couple of days ago and why'll you'll see the need for something similiar the last bootstrapper code I posted.

I'm to the point where CompositionInitializer.SatisfyImports(this); doesn't fail in this area anymore, however it is somehow getting a different instance of my viewmodels than what ImportConstructor does.

When you get it to where this sample will not crash on SatisfyImport().

Set a break point inside get set of the ModuleEView.  If you get where I'm at you'll notice that it's ModuleEController property is null even though you just set it in the ModuleEController constructor.

Hence it's pulling from a different container and then you'll be where I'm at now.

 

Developer
Oct 27, 2010 at 5:59 PM

I'll take a look.

In the meantime, here's a sample of how I could see using modularity with views and compositioninitialize.

http://cid-ab22f9941310ec60.office.live.com/richupload.aspx/Public

-b

Oct 27, 2010 at 7:08 PM

Finally the code below appears to be working. When I run CompositionInitializer.SatisfyImports(this); in my ViewModelLocator, it appears to be getting the correct Parts.

This works when I have the modules set to load WhenAvailable. I’m curious as to whether it will work with OnDemand modules.

Also, why should all the exports of a module have to go into a global container. I may have a module that just needs to import parts from my common assembly and then it’s own internal exports. Have any suggestions on this.

public class Bootstrapper : MefBootstrapper

{

private const string ModuleCatalogUri = "/EyeCue;component/ModulesCatalog.xaml";

protected override CompositionContainer CreateContainer()

{

var _container = base.CreateContainer();

this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));

this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(GlobalValues).Assembly));

CompositionHost.Initialize(_container);

return _container;

}

protected override IModuleCatalog CreateModuleCatalog()

{

IModuleCatalog _modCat = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri(ModuleCatalogUri, UriKind.Relative));

return _modCat;

}

protected override DependencyObject CreateShell()

{

return this.Container.GetExportedValue<Shell>();

}

protected override void InitializeModules()

{

base.InitializeModules();

}

protected override void InitializeShell()

{

#if SILVERLIGHT

App _app = (App)Application.Current;

_app.InitializeRootVisual(((Shell)this.Shell));

#else

Application.Current.MainWindow = (Shell)this.Shell;

Application.Current.MainWindow.Show();

#endif

IShellController _shellController = this.Container.GetExportedValue<IShellController>();

_shellController.Initialize();

}

}

Oct 27, 2010 at 7:27 PM

I went back and modified the ModularityWithMef bootstrapper and it now works as well.

Can I send same it to get added or have you guys update your sample? What we’re doing here is going to be pretty common place with LOB type apps.

I’d love for this to be available so nobody else wastes as much time as I did on this.

Oct 27, 2010 at 7:53 PM

Also, the issue I originally opened in regards to the Drop 10 assemblies throwing the following exception still exists.

Unable to locate the module with type 'EyeCue.Modules.TruckStats.TruckStatsModule, EyeCue.Modules.TruckStats, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' among the exported modules. Make sure the module name in the module catalog matches that specified on ModuleExportAttribute for the module type.

   at Microsoft.Practices.Prism.Modularity.ModuleInitializer.HandleModuleInitializationError(ModuleInfo moduleInfo, String assemblyName, Exception exception)
   at Microsoft.Practices.Prism.Modularity.ModuleInitializer.Initialize(ModuleInfo moduleInfo)
   at Microsoft.Practices.Prism.Modularity.ModuleManager.InitializeModule(ModuleInfo moduleInfo)
   at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesThatAreReadyForLoad()
   at Microsoft.Practices.Prism.Modularity.ModuleManager.IModuleTypeLoader_LoadModuleCompleted(Object sender, LoadModuleCompletedEventArgs e)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.RaiseLoadModuleCompleted(ModuleInfo moduleInfo, Exception error)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.HandleDownloadCompleted(DeploymentCatalog deploymentCatalog, AsyncCompletedEventArgs e)
   at Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader.DeploymentCatalog_DownloadCompleted(Object sender, AsyncCompletedEventArgs e)
   at System.ComponentModel.Composition.Hosting.DeploymentCatalog.OnDownloadCompleted(AsyncCompletedEventArgs e)
   at System.ComponentModel.Composition.Hosting.DeploymentCatalog.HandleOpenReadCompleted(Object sender, OpenReadCompletedEventArgs e)
   at System.Net.WebClient.OnOpenReadCompleted(OpenReadCompletedEventArgs e)
   at System.Net.WebClient.OpenReadOperationCompleted(Object arg)

If I go back to Drop 9 everything is fine.

I put all my assemblies in a folder called Prism 4 and reference to there.  Then I just update the files.  I've cleaned the solution and went and deleted all the bin & Obj directories, rebuilt.

It's got to be some kind of bug because the module's load fine and are fully functiontional.

Oct 27, 2010 at 7:55 PM

Also, the relevant portion of my ModulesCatalog.xaml file.

        <Modularity:ModuleInfo Ref="EyeCue.Modules.AppSettings.xap"
                ModuleName="EyeCue.Modules.AppSettings"
                ModuleType="EyeCue.Modules.AppSettings.AppSettingsModule, EyeCue.Modules.AppSettings, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

        <Modularity:ModuleInfo Ref="EyeCue.Modules.TruckStats.xap"
                ModuleName="EyeCue.Modules.TruckStats"
                ModuleType="EyeCue.Modules.TruckStats.TruckStatsModule, EyeCue.Modules.TruckStats, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

Developer
Oct 27, 2010 at 9:10 PM

kfrosty,

Regarding updating the MEF modularity quickstart.  We're at the end of our cycle here, so we won't be making any code changes.  Also the CompositionInitializer isn't something people necessarily use, particularly if they register view types with region or if they use the Region navigation bits.  These will automatically build up the views in the container that Prism has already configured.  I'll put a blog post together on this or post something to the Codeplex site showing how you can use these together.

Regarding the exception you're seeing.  What if you change your ModuleNames to AppSettings and TruckStats?  E.g.:

       <Modularity:ModuleInfo Ref="EyeCue.Modules.AppSettings.xap"
                ModuleName="AppSettings"
                ModuleType="EyeCue.Modules.AppSettings.AppSettingsModule, EyeCue.Modules.AppSettings, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

        <Modularity:ModuleInfo Ref="EyeCue.Modules.TruckStats.xap"
                ModuleName="TruckStats"
                ModuleType="EyeCue.Modules.TruckStats.TruckStatsModule, EyeCue.Modules.TruckStats, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

 

Oct 27, 2010 at 10:31 PM

No, changing the ModuleName doesn't resolve the problem. It looks as if the modules still load and come up correctly but I still get the exception.

Actually I decided to check the Output window and you even see the symbols loading for the Modules.

'iexplore.exe' (Silverlight): Loaded 'EyeCue.Modules.AppSettings', Symbols loaded.

'iexplore.exe' (Silverlight): Loaded 'EyeCue.Modules.TruckStats', Symbols loaded.

Developer
Oct 28, 2010 at 12:47 AM

How are you exporting your modules?  What's the ExportModule attribute look like?

Oct 28, 2010 at 12:49 AM

[ModuleExport(typeof(TruckStatsModule), InitializationMode = InitializationMode.WhenAvailable)]

public class TruckStatsModule : IModule

[ModuleExport(typeof(AppSettingsModule), InitializationMode = InitializationMode.WhenAvailable)]

public class AppSettingsModule : IModule

Developer
Oct 28, 2010 at 1:23 AM

Try this instead:

        <Modularity:ModuleInfo Ref="EyeCue.Modules.AppSettings.xap"
                ModuleName="AppSettingsModule"
                ModuleType="EyeCue.Modules.AppSettings.AppSettingsModule, EyeCue.Modules.AppSettings, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

        <Modularity:ModuleInfo Ref="EyeCue.Modules.TruckStats.xap"
                ModuleName="TruckStatsModule"
                ModuleType="EyeCue.Modules.TruckStats.TruckStatsModule, EyeCue.Modules.TruckStats, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

 

Bold indicates change.

Oct 28, 2010 at 1:28 AM

FYI. I have to change the assemblies each time you ask me to try this and I will if I have too. However, what you are asking me to do, wouldn't that indicate a bug with Prism?

Why would it matter what the name is? Also, even if that were the issue, the modules still load and prism is throwing exception claiming that it couldn't find them.

The name of the modules are EyeCue.Modules.TruckStatModule & .AppSettingModule

Oct 28, 2010 at 1:37 AM

The last change seems to gotten rid of the problem. I would still consider this a bug if nothing else the exception being shown should be more accurate instead stating the module wasn't loaded.