Usage with `AssemblyBuilder`/`MetadataLoadContext`
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.
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;
}
}