Skip to content

BAML parser resolves assemblies ignoring AssemblyLoadContext #11848

Description

@cunhamauro

BAML assembly resolution and AssemblyLoadContext

The problem

WPF compiles XAML into BAML. If BAML contains references to assemblies then they need to be resolved.

That resolution must be compatible with .NET assembly-loading semantics. In particular, an application may load assemblies into different AssemblyLoadContext (ALC) instances to isolate extensions, versions, tenants, or other independently loaded components.

Type identity includes the load context

An assembly name and version alone do not establish type compatibility. Two assemblies with the same name and version can be loaded into different ALCs resulting in distinct identities and consequently different runtime types.

Why BAML needs ALC awareness

A BAML reference normally identifies an assembly by its name and version. If more than one loaded assembly in the global appdomain satisfies that criteria, it becomes ambiguous. Selecting the last loaded matching assembly from a process-wide set of loaded assemblies can bind BAML to a type from an unrelated ALC.

The parser must instead resolve the reference in the loading context appropriate to the BAML-owning assembly. This preserves the normal rule that code and the BAML it owns refer to the same component-local types.

Observable symptoms

An incorrect resolution often does not look like an assembly-load failure. The types can have identical full names and assembly-qualified names, yet fail equality or assignability checks. Typical WPF symptoms include:

  • An implicit DataTemplate is not selected for an object whose type appears correct in logs.
  • A resource keyed by {x:Type ...} cannot be found.
  • A style, converter, markup extension, or custom control is resolved from an unexpected component.
  • A cast or type comparison fails even though both types display the same namespace and class name.

These symptoms are consequences of type identity, not of a missing XAML resource.

Contextual reflection is not a solution

AssemblyLoadContext.EnterContextualReflection() helps APIs that explicitly use the current contextual-reflection context. It does not by itself repair a resolution path that ignores the BAML owner's context or that performs a process-wide assembly lookup. The BAML resolution implementation must carry or derive the correct context itself.

Similarly, a global assembly-resolution event can choose an assembly for an unresolved request, but it cannot make types loaded from different ALCs compatible after the fact.

Required behaviour

When resolving a type encoded in BAML, WPF should:

  1. Identify the assembly that owns the BAML being parsed.
  2. Determine the AssemblyLoadContext associated with that assembly.
  3. Resolve referenced assemblies and types using that context, following the host's loading policy.
  4. Avoid substituting an already loaded, name-matching assembly from another ALC merely because it has the same identity.

The aim is not to unify types across ALCs. It is to preserve .NET's existing type-identity and isolation rules while ensuring BAML resolves to the instance the owning component expects.

Reproduction Steps

How to reproduce
A minimal repro is available at XamlParserTypeIdentityMismatch.

  1. Clone the repository and open the solution.
  2. In Revit/Program.cs, set LoadSampleLibrariesInDefaultALC to false.
  3. Build and run the Revit project.
    In this configuration, each add-in loads its own instance of the same assembly into its own AssemblyLoadContext. Although the assemblies have the same name and version, their types have different identities because they are loaded in different ALCs.
    The BAML parser resolves the type reference to the assembly instance that was loaded most recently. As a result, the BAML-resolved type matches one add-in but not the other. In the affected add-in, the implicit DataTemplate no longer matches the ViewModel, even though both types have the same displayed type name.
    If LoadSampleLibrariesInDefaultALC is set to true, both add-ins share the same assembly instance in the default ALC, and the issue no longer occurs.

Expected behavior

When resolving a type reference from BAML, WPF should load or resolve the referenced assembly in the AssemblyLoadContext that owns the requesting assembly, the assembly that contains the BAML being parsed.
This ensures that BAML-resolved types have the same runtime type identity as the types used by the requesting add-in, even when assemblies with the same name and version are loaded in multiple ALCs.

Actual behavior

When multiple instances of the same assembly are loaded in different AssemblyLoadContexts, BAML resolves the referenced assembly from the most recently loaded matching instance rather than from the ALC that owns the requesting assembly.
As a result, BAML may resolve a type from a different ALC. The type has the same name and assembly identity, but a different runtime type identity, causing type comparisons and implicit DataTemplate matching to fail.

Regression?

This issue did not occur on .NET Framework because assemblies were loaded into a single AppDomain assembly-loading scope. Within that scope, there could only be one loaded instance of a given assembly identity (name, version, culture, and public key token).
AssemblyLoadContext in modern .NET allows multiple instances of the same assembly name and version combination to be loaded into the same appdomain.

Known Workarounds

One workaround is to move XAML type references into code-behind. For example, a DataTemplate, control, or any other assembly referenced in XAML can be created or assigned in managed code, where the application can explicitly use the type loaded by the intended AssemblyLoadContext.
This is not a complete solution. It does not protect against transitive XAML references or internal framework logic that resolves types from BAML and encounters the same ambiguity.
Another workaround is assembly deduplication: ensure that only one instance of each assembly name and version combination is loaded in the entire application domain. This removes the ambiguity because there is only one matching assembly available for BAML resolution.
In a multi-add-in application, however, global deduplication is difficult to enforce. Third party add-ins may have different loading policies which violate deduplication and result in the same conflict.

Impact

This issue can cause widespread type-identity conflicts in add-in and plug-in applications, where multiple assemblies must coexist in the same process while remaining isolated in separate AssemblyLoadContexts.

Configuration

Any .NET version.

Other information

I have identified the exact source of this issue, implemented a fix (#11849), and validated it locally using the reproduction project: https://github.com/cunhamauro/XamlParserTypeIdentityMismatch

Read more about this @:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Enhancement RequestedProduct code improvement that does NOT require public API changes/additions

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions