Skip to content

TypeDescriptor.GetConverter call always uses AssemblyLoadContext.Default #68520

Open
@AndreyTretyak

Description

@AndreyTretyak

Description

From the assembly loaded into Custom AssemblyLoadContext we are calling TypeDescriptor.GetConverter(Type) method. This method is defined in System.ComponentModel.TypeConverter.dll that is part of .NET 6 runtime, so it’s loaded into AssemblyLoadContext.Default.
Inside of the the TypeDescriptor.GetConverter(Type) there is a call to Type.GetType(string) for the type from the assembly that is loaded in Custom AssemblyLoadContext. So, unfortunately, it fails to get this type.
Calling Type.GetType(string) without TypeDescriptor.GetConverter(Type) in the same place from the assembly loaded in from the Custom AssemblyLoadContext returns type properly.

Reproduction Steps

To simplify repro I'll also add link to zip file with the sample solution.

Below is the description of the process to create this solution:

You need two projects to reproduce the issue. The first one will create an assembly (the TestAssembly) and the second one will run it in the Custom AssemblyLoadContext (the AssemblyLoadContextRepro).

TestAssembly code:

using System;
using System.ComponentModel;

namespace TestAssembly
{
  public class Test
  {
    public static void Run()
    {
      // Returns type from the Current AssemblyLoadContext properly.
      var type = Type.GetType("TestAssembly.MyClassConverter, TestAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

      // Tries to get type from AssemblyLoadContext.Default and fails because of it.
      var fromConverter = TypeDescriptor.GetConverter(typeof(MyClass));
    }
  }

  [TypeConverter(typeof(MyClassConverter))]
  public class MyClass { }

  public class MyClassConverter : TypeConverter { }
}

AssemblyLoadContextRepro that would load assembly:

using System.Runtime.Loader;
using System.Reflection;
using System;
using System.IO;

namespace AssemblyLoadContextRepro
{
  internal class Program
  {
    static void Main(string[] args)
    {
      var context = new TestAssemblyLoadContext();
      var assembly = context.LoadFromAssemblyName(new AssemblyName("TestAssembly"));
      var type = assembly.GetType("TestAssembly.Test");
      var method = type.GetMethod("Run");
      method.Invoke(null, Array.Empty<object>());
    }
  }

  internal class TestAssemblyLoadContext : AssemblyLoadContext
  {
    private string assembliesLocation;

    public TestAssemblyLoadContext() : base("Custom")
    {
      // path to binaries of TestAssembly project.
      assembliesLocation = Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\..\TestAssembly\bin\Debug\net6.0\");
    }

    protected override Assembly Load(AssemblyName assemblyName)
    {
      var path = Path.Combine(assembliesLocation, assemblyName.Name + ".dll");
      if (File.Exists(path))
      {
        var assembly = this.LoadFromAssemblyPath(path);
        return assembly;
      }

      return null;
    }
  }
}

Expected behavior

I would prefer TypeDescriptor.GetConverter(Type) to behave in the same way as Type.GetType(string) and use AssemblyLoadContext of the code that calling it if possible. Because System.ComponentModel.TypeConverter.dll is distributed with .NET runtime and would be always loaded in AssemblyLoadContext.Default that makes it unusable for custom contexts.

Actual behavior

TypeDescriptor.GetConverter(Type) always use AssemblyLoadContext.Default and as a result unable to load converters from assemblies in Custom AssemblyLoadContext.

Regression?

No response

Known Workarounds

Wrapping call to into EnterContextualReflection allows it to work correctly:

using (AssemblyLoadContext.EnterContextualReflection(Assembly.GetExecutingAssembly())) 
{
 TypeDescriptor.GetConverter(type);
}

Not working workarounds:

  • Loading assemblies into AssemblyLoadContext.Default defeats the purpose of having separate contexts.
  • Loading System.ComponentModel.TypeConverter.dll into Custom AssemblyLoadContext allows Type.GetType(string) to get converter type, but we end up with the same assembly loaded into both contexts that make it unusable, for example, resulted type could not be assigned to ITypeConverter, because typeof(ITypeConverter) returns type from the System.ComponentModel.TypeConverter.dll in AssemblyLoadContext.Default. So TypeDescriptor.GetConverter(Type) fails to return converter in this case also.

Configuration

No response

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions