Unity module loading requires exact dependency versions

Topics: Prism v4 - WPF 4
Aug 20, 2012 at 6:10 PM

Our scenario is:

Our product uses prism/unity to load modules.  Most of our projects target the .NET 4.0 framework, but some of them target the 3.5 framework (for reuse on mobile platforms).  The 3.5 projects have the "specific version" property of all of their references set to false.  This should allow the 3.5 projects to run under just the 4.0 framework.  However, when using the module loading functionality, we get exceptions stating that the 3.5 framework versions of System.Core etc cannot be found.  

The stack trace of the exception is below.  Note the GetExportedTypes call which ends up calling Assembly.ReflectionOnlyLoad.  According to various documentation and blogs, I see that ReflectionOnlyLoad does not take publishers binding redirection into account.  This means that you need to have the exact version of the .NET framework installed on the target machine when using unity module initialization.  Am I correct here?  Are there any work-arounds?


Module Initialization Exception
Module Initialization exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Core, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The system cannot find the file specified.
File name: 'System.Core, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089' ---> System.IO.FileNotFoundException: Could not load file or assembly 'System.Core, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The system cannot find the file specified.
File name: 'System.Core, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089'
   at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.ReflectionOnlyLoad(String assemblyString)
   at Microsoft.Practices.Prism.Modularity.DirectoryModuleCatalog.InnerModuleInfoLoader.OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
   at System.AppDomain.OnReflectionOnlyAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)

=== Pre-bind state information ===
LOG: User = WIN-2SU06FE6CED\user
LOG: DisplayName = System.Core, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089
LOG: Appbase = file:///C:/Users/user/AppData/Local/Apps/2.0/PX0L6PA0.K12/CX3ZDEO7.D8K/itro..tion_7bbd5db6ed1178b5_0003.0002_e3dc8f8b12f96421/
LOG: Initial PrivatePath = NULL
Calling assembly : (Unknown).
LOG: This is an inspection only bind.
LOG: Using application configuration file: C:\Users\user\AppData\Local\Apps\2.0\PX0L6PA0.K12\CX3ZDEO7.D8K\itro..tion_7bbd5db6ed1178b5_0003.0002_e3dc8f8b12f96421\company.Fdm.Windows.Client.exe.config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Attempting download of new URL file:///C:/Users/user/AppData/Local/Apps/2.0/PX0L6PA0.K12/CX3ZDEO7.D8K/itro..tion_7bbd5db6ed1178b5_0003.0002_e3dc8f8b12f96421/System.Core.DLL.
LOG: Attempting download of new URL file:///C:/Users/user/AppData/Local/Apps/2.0/PX0L6PA0.K12/CX3ZDEO7.D8K/itro..tion_7bbd5db6ed1178b5_0003.0002_e3dc8f8b12f96421/System.Core/System.Core.DLL.
LOG: Attempting download of new URL file:///C:/Users/user/AppData/Local/Apps/2.0/PX0L6PA0.K12/CX3ZDEO7.D8K/itro..tion_7bbd5db6ed1178b5_0003.0002_e3dc8f8b12f96421/System.Core.EXE.
LOG: Attempting download of new URL file:///C:/Users/user/AppData/Local/Apps/2.0/PX0L6PA0.K12/CX3ZDEO7.D8K/itro..tion_7bbd5db6ed1178b5_0003.0002_e3dc8f8b12f96421/System.Core/System.Core.EXE.

   at System.Reflection.RuntimeAssembly.GetExportedTypes(RuntimeAssembly assembly, ObjectHandleOnStack retTypes)
   at System.Reflection.RuntimeAssembly.GetExportedTypes()
   at Microsoft.Practices.Prism.Modularity.DirectoryModuleCatalog.InnerModuleInfoLoader.<>c__DisplayClassf.b__b(FileInfo file)
   at System.Linq.Enumerable.d__14`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Microsoft.Practices.Prism.Modularity.DirectoryModuleCatalog.InnerModuleInfoLoader.GetModuleInfos(String path)
   at Microsoft.Practices.Prism.Modularity.DirectoryModuleCatalog.InnerModuleInfoLoader.GetModuleInfos(String path)
   at Microsoft.Practices.Prism.Modularity.DirectoryModuleCatalog.InnerLoad()
   at Microsoft.Practices.Prism.Modularity.ModuleCatalog.Initialize()
   at Microsoft.Practices.Prism.Modularity.ModuleManager.Run()
   at Microsoft.Practices.Prism.UnityExtensions.UnityBootstrapper.InitializeModules()
   at company.Fdm.Windows.Client.Bootstrapper.InitializeModules()


Aug 21, 2012 at 5:55 PM


Based on my understanding the cause of this error is that the "Specific Version” property is only a build-time directive (used by Visual Studio). It has no effect on the run-time version resolution of the referenced assembly. Hence, when you run the application, the run-time attempts to bind with the exact version of the assembly that the module was built with, causing the error you mentioned.

Regarding this topic, I believe you could also find the following resources interesting:

Also, as far as I know, the aforementioned scenario is not supported in Prism as out of the box, as Prism doesn't provide any mechanism to control the versions of the assemblies of each module. In my opinion, usually it's expected that all the modules consumed by the application are compatible between each other and the shell application itself, and that the versions of the assemblies they share are the same.


Agustin Adami

Aug 21, 2012 at 10:41 PM

Thanks for the reply Agustin.  I want to clarify a couple of things.

"Specific Version is set to false."  This implies (at least with .NET assemblies) that, depending on the publishers policies, during runtime, a more current version can be used if the exact version is not located.  So if I built against System.Core 3.5 but only have System.Core 4.0 on my system, the assembly will bind to the 4.0 version.  

Assembly.ReflectionOnlyLoad does not inspect the publishers policies, but requires that the exact version be present on the system.  Assembly.Load DOES inspect the publishers policies and will allow a binding redirect.

The DirectoryModuleCatalog does a ReflectionOnlyLoad in order to determine which modules are available in the module directory.  The use of ReflectionOnlyLoad puts a restriction on module loading that any referenced assemblies must be the same version that it was built against.  Even though during a normal Assembly.Load it may successfully bind to a more current version.

Am I correct so far?  If so, it would seem to me that the DirectoryModuleCatalog could change to use Assembly.Load instead of ReflectionOnlyLoad.  The ModuleCatalog uses a temporary AppDomain and disposes it after discovering all modules, so there shouldn't be a concern of assemblies being left loaded.  I have done this in a local copy of Microsoft.Practices.Prism.dll and this seems to correct the problem.





Aug 22, 2012 at 7:01 PM

Hi Jeff,

Thanks for sharing your findings. I believe you could create a work item in the Issue Tracker section as a suggestion portraying them, so that the team can analyze it and take it into account for future releases.

Also, take into account that, as far as I know, you can define your custom implementation of a DirectoryModuleCatalog without needing to change the Prism Library's source code. You can find more about this in the following chapter of the Prism documentation:


Damian Cherubini

Aug 22, 2012 at 11:02 PM

Thank you Damian.  I have created an issue and have created a custom class which implements the changes I suggested for our use.