Vitor P. Carneiro
Vitor P. Carneiro

Reputation: 56

Unable to get MetadataReference's for CSharpCompilation on .NET MAUI for Android

I want to compile some classes at runtime, but to do so I need to reference some assemblies like System.Linq.dll or System.Collections.dll.

The problem is that I can't use MetadataReference.CreateFromFile because Assembly.Location only returns the assembly name and not a file path when the solution is built in Release mode.

Is there any alternative?

I've already tried some solutions found online but none of them worked, such as:

var methodInfo = assembly.GetType().GetMethod("GetRawBytes", BindingFlags.Instance | BindingFlags.NonPublic);
byte[] assemblyBytes = (byte[])methodInfo.Invoke(assembly, null);
var reference = MetadataReference.CreateFromImage(assemblyBytes);

The problem with this one is that GetRawBytes no longer exists in newer .NET versions github.com/dotnet/runtime/issues/28132#issue-558436811

var hash = new Hash(assembly);
var dllAsArray = (byte[])hash.GetType().GetMethod("GetRawData", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(hash, new object[0]);
var reference = MetadataReference.CreateFromImage(dllAsArray);

The problem with this one is that Hash() only works on Windows.

var formatter = new BinaryFormatter();
var ms = new MemoryStream();
formatter.Serialize(ms, assembly);
var reference = MetadataReference.CreateFromImage(ms.GetBuffer());

The problem with this one is that BinaryFormatter is obsolete and raises an error in compile time.

I also found this repo on github which seems to have the solution to work on Android, but when trying to reproduce this in my app I get a NotImplementedException calling the TryGetRawMetadata() extension method.

Upvotes: 0

Views: 90

Answers (1)

Vitor P. Carneiro
Vitor P. Carneiro

Reputation: 56

I found a workaround. It's not the best solution, but it's working.

First I added a post-build event to copy the resulting DLLs to a project directory:

xcopy "$(ProjectDir)obj\$(Configuration)\$(TargetFramework)\android\assets\*.dll" "$(ProjectDir)Resources\Libraries" /Y 

I then defined the required DLLs as Maui Asset in the .csproj file. For example:

<ItemGroup>
  <None Remove="Resources\Libraries\System.Linq.dll" />
</ItemGroup>
<ItemGroup>
  <MauiAsset Include="Resources\Libraries\System.Linq.dll" />
</ItemGroup>

Finally, I'm loading the reference with the following:

using var stream = await FileSystem.OpenAppPackageFileAsync("Resources/Libraries/System.Linq.dll");
using var ms = new MemoryStream();
stream.CopyTo(ms);
var reference = MetadataReference.CreateFromImage(ms.GetBuffer());

Upvotes: 0

Related Questions