Trying to load XAML into non-default AssemblyLoadContext can lead to very confusing errors. The underlying problem is that XAML parser is not aware of assembly load contexts. The parser itself typically runs in the Default load context, but it may be triggered to load XAML into a custom (secondary) load context. In that case all assembly resolution should happen via the secondary load context.
In order to make this easier .NET Core 3.0 introduced the "contextual reflection" concept, which can switch all reflection based APIs to use the secondary load context. More details about contextual reflection can be found in AssemblyLoadContext.ContextualReflection.md.
That fixes all cases where the XAML parser uses reflection APIs like Assembly.GetType and similar. Unfortunately the XAML parser implements its own assembly resolution logic in some cases. This logic was copied from the .NET Framework version of WPF and it still relies AppDomains, GAC and so on - it is not aware of AssemblyLoadContext. This logic can break the correct behavior: Among other things it walks all assemblies loaded into the current AppDomain (so all assemblies in the process, as .NET Core has only one AppDomain) and using custom logic resolves assembly against that list. If there are two assemblies of the same (or similar) names in that list, it will basically randomly pick the first one it finds. The code which does that is here:
|
internal static Assembly GetLoadedAssembly(AssemblyName assemblyName) |
Assembly load contexts are typically used to implement plugin architecture. To provide good levels of isolation for each plugin, every plugin is loaded into its own load context. This can very easily lead to cases where each plugin has its own version of a certain dependency. But the above mentioned code ignores the isolation of load contexts, and will resolve assembly globally - leading to cases where the plugins will get the wrong version of dependency used.
A sample repro app is here: https://github.com/vitek-karas/WPFPluginLoadProblem
This app shows the problem with a typical plugin architecture (host app loading two plugins, each using XAML parser to load some XAML).
Originally this problem was found trying to implement tests on WPF, using ALC to provide isolation of WPF itself. The repro of that case is here: https://github.com/nick-beer/ALC-XAML-LOAD-BUG
This repro boils down to the same underlying problem.
/cc @nick-beer
Trying to load XAML into non-default
AssemblyLoadContextcan lead to very confusing errors. The underlying problem is that XAML parser is not aware of assembly load contexts. The parser itself typically runs in theDefaultload context, but it may be triggered to load XAML into a custom (secondary) load context. In that case all assembly resolution should happen via the secondary load context.In order to make this easier .NET Core 3.0 introduced the "contextual reflection" concept, which can switch all reflection based APIs to use the secondary load context. More details about contextual reflection can be found in AssemblyLoadContext.ContextualReflection.md.
That fixes all cases where the XAML parser uses reflection APIs like
Assembly.GetTypeand similar. Unfortunately the XAML parser implements its own assembly resolution logic in some cases. This logic was copied from the .NET Framework version of WPF and it still relies AppDomains, GAC and so on - it is not aware ofAssemblyLoadContext. This logic can break the correct behavior: Among other things it walks all assemblies loaded into the current AppDomain (so all assemblies in the process, as .NET Core has only one AppDomain) and using custom logic resolves assembly against that list. If there are two assemblies of the same (or similar) names in that list, it will basically randomly pick the first one it finds. The code which does that is here:wpf/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/SafeSecurityHelper.cs
Line 133 in ac9d1b7
Assembly load contexts are typically used to implement plugin architecture. To provide good levels of isolation for each plugin, every plugin is loaded into its own load context. This can very easily lead to cases where each plugin has its own version of a certain dependency. But the above mentioned code ignores the isolation of load contexts, and will resolve assembly globally - leading to cases where the plugins will get the wrong version of dependency used.
A sample repro app is here: https://github.com/vitek-karas/WPFPluginLoadProblem
This app shows the problem with a typical plugin architecture (host app loading two plugins, each using XAML parser to load some XAML).
Originally this problem was found trying to implement tests on WPF, using ALC to provide isolation of WPF itself. The repro of that case is here: https://github.com/nick-beer/ALC-XAML-LOAD-BUG
This repro boils down to the same underlying problem.
/cc @nick-beer