Skip to content

Usage with AssemblyBuilder/MetadataLoadContext #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Windows10CE opened this issue Mar 15, 2024 · 1 comment
Open

Usage with AssemblyBuilder/MetadataLoadContext #49

Windows10CE opened this issue Mar 15, 2024 · 1 comment

Comments

@Windows10CE
Copy link

Windows10CE commented Mar 15, 2024

As mentioned in the new documentation for persistable dynamic assemblies in .NET 9 here, to create assemblies for specific TFMs (or assemblies that can actually be used as references in modern C# projects), the new API requires you to create a MetadataLoadContext using the reference assemblies for that TFM. As is already noted by this package's README, this can be challenging!

Adding a bit of documentation that myself and others can point to (or even a helper method to create a MetadataLoadContext like exists for Compilations) would help a lot (I am completely willing to contribute this myself).

This also currently always pulls in a dependency for Microsoft.CodeAnalysis.Common, which if you just want the embedded resources for the assembly files (for example to use for this, or other applications like loading them with AsmResolver.DotNet or MetadataReader) can cause a fairly large transitive dependency that isn't wanted. I completely understand if this is out of scope for what you have here, just let me know if that's the case, I'll probably end up creating a fork of what you have as a separate package.

@jnm2
Copy link

jnm2 commented Feb 24, 2025

Here's some code for those who just want to get spinning with MetadataLoadContext:

using System.Reflection;

using var context = CreateMetadataLoadContextWithAllAssembliesLoaded(
    from reference in Basic.Reference.Assemblies.Net90.ReferenceInfos.All
    select (reference.FileName, reference.ImageBytes));

foreach (var assembly in context.GetAssemblies().OrderBy(a => a.GetName().Name))
{
    Console.WriteLine($"{assembly.GetName().Name}: {assembly.GetExportedTypes().Length} public types");
}

static MetadataLoadContext CreateMetadataLoadContextWithAllAssembliesLoaded(
    IEnumerable<(string FileName, byte[] ImageBytes)> references)
{
    var resolver = new BasicReferenceAssembliesResolver(references);
    var context = new MetadataLoadContext(resolver);

    foreach (var assemblyName in resolver.AvailableAssemblyNames)
        context.LoadFromAssemblyName(assemblyName);

    return context;
}

internal sealed class BasicReferenceAssembliesResolver : MetadataAssemblyResolver
{
    private readonly Dictionary<string, byte[]> imageBytesByAssemblyName = new(StringComparer.OrdinalIgnoreCase);

    public IReadOnlyCollection<string> AvailableAssemblyNames => imageBytesByAssemblyName.Keys;

    public BasicReferenceAssembliesResolver(
        IEnumerable<(string FileName, byte[] ImageBytes)> references)
    {
        foreach (var reference in references)
        {
            if (!reference.FileName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                throw new ArgumentException("Reference assembly file names are all expected to end with .dll.", nameof(references));

            if (!imageBytesByAssemblyName.TryAdd(reference.FileName[..^".dll".Length], reference.ImageBytes))
                throw new ArgumentException("Reference assemblies are expected to have unique names.", nameof(references));
        }
    }

    public override Assembly? Resolve(MetadataLoadContext context, AssemblyName assemblyName)
    {
        return assemblyName.Name is { } name && imageBytesByAssemblyName.TryGetValue(name, out var imageBytes)
            ? context.LoadFromByteArray(imageBytes)
            : null;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants