Using MEF to download Modules and discover parts

Topics: Prism v4 - Silverlight 4
Jul 20, 2010 at 2:45 PM

I've been looking at the ModularityWithMef.Silverlight Quickstart. I assumed that when a module was downloaded, MEF would discover any Exports within the module and resatisfy any appropriate imports. However, if I do a simple test I can only get this to work on Module A and Module C. These are the modules that referenced by the main bootstrapper project.

I'm assuming that when a dynamic module is downloaded, either by demand on when available, its parts are not added to the aggregate catalog.

Does anyone know where/how I go about doing this?



Jul 20, 2010 at 3:36 PM

I've discovered where I think the problem lies, but have no idea if this is intentional...

Within the MefXapModuleTypeLoader.DeploymentCatalog_DownloadCompleted method, the DeploymentCatalogue is having its export definitions filtered so that only the export for IModule is being added to the aggregate catalog.

private void DeploymentCatalog_DownloadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
            DeploymentCatalog deploymentCatalog = sender as DeploymentCatalog;
            if (deploymentCatalog != null)
                // Because the XAPs loaded will contain assemblies that have references to this assembly, 
                // and because this assembly exports singletons, I must filter to only modules to prevent DuplicateAssemblyExceptions.
                FilteredCatalog filteredCatalog = new FilteredCatalog(deploymentCatalog,
                       x => x.ExportDefinitions.Where(def => def.ContractName == AttributedModelServices.GetContractName(typeof(IModule))).SingleOrDefault() != null);


                // I lock the registry since the BeginLoadModuleType adds the entry on the foreground thread.
                List<ModuleLoadCallbackInfo> callbackInfos;
                lock (this.callbackRegistry)
                    callbackInfos = this.callbackRegistry[deploymentCatalog.Uri];

                foreach (ModuleLoadCallbackInfo callbackInfo in callbackInfos)
                    // This transitions the the foreground thread.
                    Deployment.Current.Dispatcher.BeginInvoke(callbackInfo.LoadedCallback, new ModuleLoadedEventArgs(callbackInfo.ModuleInfo, e.Error));     

I can see why this has been done, but it does look like this implementation can only ever work for exporting modules. I guess it assumes you will use Prism and the module initialised to register regions etc against the unity container.

Does anyone have any suggestions as to how I can use MEF to download the modules, but add any discovered parts to the aggregate catalog.

Jul 20, 2010 at 5:04 PM

This was actually done intentionally, but we have been looking for a work around.  Basically, due to how we are doing the overridable default implementations of the core Prism services (event aggregator, module catalog, etc) and how MEF will import a dll and all its dependencies.  If we do not filter the types imported, then MEF will see the module's assembly and the assemblies it references and pull in all the exported types from those other assemblies.  This leads to re-importing the Prism core services, overwriting the previously created instances, and basically causing everything to blow up.

I do have a bug filed on this.  This morning we had another conversation about possible solutions to this, and may have come up with one or two that will work.

Hopefully you will see a fix to this in one of our future drops.

Aug 12, 2010 at 3:30 PM

I've upgraded to Prism 4.0 CTP, but I still get an error message. If I change the linq to FirstOrDefault it works.

Aug 12, 2010 at 5:17 PM

Can you change the references to the Prism DLLs in your modules projects to have "Copy Local" set to false? 

If so, that should stop the multiple imports of the core services.

Also note that this is continuing to change, and our next drop may have a few things working differently to avoid the issue.