A Combined Module Enumerator For The Prism Bootstrapper
Thursday, December 4, 2008 – 4:00 AMI ran into the following issue with p&p’s Composite WPF guidance (Prism). Prism allows you to configure your application with multiple modules. These can either be statically referenced or dynamically discovered at runtime
In my scenario I have two assemblies each containing a module; assembly A and assembly B, containing modules A and B respectively. Assembly A also contains a type T which is used elsewhere in the my main application. So my application EXE has a reference to Assembly A and loads it (statically). On the other hand B can be loaded dynamically at runtime simply by placing it in the correct directory.
My implementation of the Prism bootstrapper overrides GetModuleEnumerator and returns a DirectoryLookupModuleEnumerator. I naively expected this to load both module A and B because they are both in the directory searched by the directory lookup enumerator. Even though Module A had already been loaded statically I expected the enumerator to return it in the list of loaded/loadable modules.
Here’s the gotcha… The directory lookup enumerator ignores types in already loaded assemblies.
So what’s the solution? It turns out you have to write a custom enumerator to merge the static and dynamically loaded modules.
Writing your own module enumerator that implements IModuleEnumerator and returns the contents of both the static and directory lookup enumerators is pretty easy. You simply replace your call in the bootstrapper to use a new enumerator. The bootstrapper would look something like this.
protected override IModuleEnumerator GetModuleEnumerator() { IModuleEnumerator enumerator = new CombinedModuleEnumerator( Directory.GetCurrentDirectory()); return enumerator; }
Here’s my implementation of CombinedModuleEnumerator. It simply merges the results from a static and directory based enumerator. The second constructor is largely there for unit testing purposes (in my tests I pass in two stub implementations of IModuleEnumerator.
public class CombinedModuleEnumerator : IModuleEnumerator { private readonly IModuleEnumerator _directoryEnumerator; private readonly IModuleEnumerator _staticEnumerator; public CombinedModuleEnumerator(string path) : this(new StaticModuleEnumerator(), new DirectoryLookupModuleEnumerator(path)) { } public CombinedModuleEnumerator(IModuleEnumerator staticEnumerator, IModuleEnumerator directoryEnumerator) { _staticEnumerator = staticEnumerator; _directoryEnumerator = directoryEnumerator; } public ModuleInfo[] GetModules() { ModuleInfo[] m1 = _directoryEnumerator.GetModules(); ModuleInfo[] m2 = _staticEnumerator.GetModules(); return MergeModuleInfoArrays(m1, m2); } public ModuleInfo[] GetStartupLoadedModules() { ModuleInfo[] m1 = _directoryEnumerator.GetStartupLoadedModules(); ModuleInfo[] m2 = _staticEnumerator.GetStartupLoadedModules(); return MergeModuleInfoArrays(m1, m2); } public ModuleInfo GetModule(string moduleName) { ModuleInfo m1 = _staticEnumerator.GetModule(moduleName); return m1 ?? _directoryEnumerator.GetModule(moduleName); } private static ModuleInfo[] MergeModuleInfoArrays( ModuleInfo[] m1, ModuleInfo[] m2) { if (IsEmpty(m1)) return m2; if (IsEmpty(m2)) return m1; ModuleInfo[] m = new ModuleInfo[m1.Length + m2.Length]; m1.CopyTo(m, 0); m2.CopyTo(m, m1.Length - 1); return m; } private static bool IsEmpty(ModuleInfo[] modules) { return (modules == null) || (modules.Length == 0); } }
That’s it. Obviously there are a lot of variations you could implement on this theme and the code above isn’t designed to be a completely general solution. Having multiple directory based enumerators for different types of modules for example. This covers the simplest scenario allowing your application to have both static and dynamic (directory based) modules.
Sorry, comments for this entry are closed at this time.