I'm trying to use CSharpScript in a Blazor wasm application, testing with a simple EvaluateAsync
:
var result = await CSharpScript.EvaluateAsync<int>("1 + 1");
Throws: System.IO.FileNotFoundException: Could not find file "/mscorlib.dll"
I'm using Blazor wasm 3.2.0-preview3.20168.3
Edit:
Here's the full code in index.razor
:
@code{
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Console.WriteLine("Initializing...");
var result = await CSharpScript.EvaluateAsync<int>("1 + 1");
}
}
And here's the console output:
Initializing...
blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Could not find file "/mscorlib.dll"
System.IO.FileNotFoundException: Could not find file "/mscorlib.dll"
File name: '/mscorlib.dll'
at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) <0x3ba83f8 + 0x002b4> in <filename unknown>:0
at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) <0x3b987a0 + 0x0001c> in <filename unknown>:0
at System.IO.File.OpenRead (System.String path) <0x3b986d0 + 0x0000a> in <filename unknown>:0
at Roslyn.Utilities.FileUtilities.OpenFileStream (System.String path) [0x0001c] in /_/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs:416
at Microsoft.CodeAnalysis.MetadataReference.CreateFromAssemblyInternal (System.Reflection.Assembly assembly, Microsoft.CodeAnalysis.MetadataReferenceProperties properties, Microsoft.CodeAnalysis.DocumentationProvider documentation) [0x0005a] in /_/src/Compilers/Core/Portable/MetadataReference/MetadataReference.cs:329
at Microsoft.CodeAnalysis.MetadataReference.CreateFromAssemblyInternal (System.Reflection.Assembly assembly) [0x00000] in /_/src/Compilers/Core/Portable/MetadataReference/MetadataReference.cs:271
at Microsoft.CodeAnalysis.Scripting.Script.GetReferencesForCompilation (Microsoft.CodeAnalysis.CommonMessageProvider messageProvider, Microsoft.CodeAnalysis.DiagnosticBag diagnostics, Microsoft.CodeAnalysis.MetadataReference languageRuntimeReferenceOpt) [0x0001a] in /_/src/Scripting/Core/Script.cs:252
at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScriptCompiler.CreateSubmission (Microsoft.CodeAnalysis.Scripting.Script script) [0x00021] in /_/src/Scripting/CSharp/CSharpScriptCompiler.cs:40
at Microsoft.CodeAnalysis.Scripting.Script.GetCompilation () [0x00008] in /_/src/Scripting/Core/Script.cs:144
at Microsoft.CodeAnalysis.Scripting.Script`1[T].GetExecutor (System.Threading.CancellationToken cancellationToken) [0x00008] in /_/src/Scripting/Core/Script.cs:361
at Microsoft.CodeAnalysis.Scripting.Script`1[T].RunAsync (System.Object globals, System.Func`2[T,TResult] catchException, System.Threading.CancellationToken cancellationToken) [0x0001b] in /_/src/Scripting/Core/Script.cs:465
at Microsoft.CodeAnalysis.Scripting.Script`1[T].RunAsync (System.Object globals, System.Threading.CancellationToken cancellationToken) [0x00000] in /_/src/Scripting/Core/Script.cs:439
at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync[T] (System.String code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options, System.Object globals, System.Type globalsType, System.Threading.CancellationToken cancellationToken) [0x00000] in /_/src/Scripting/CSharp/CSharpScript.cs:93
at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync[T] (System.String code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options, System.Object globals, System.Type globalsType, System.Threading.CancellationToken cancellationToken) [0x00000] in /_/src/Scripting/CSharp/CSharpScript.cs:123
at ScriptPlayground.Pages.Index.OnInitializedAsync () [0x0008a] in C:\Users\sarma\source\repos\ScriptPlayground\Pages\Index.razor:17
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync () <0x36de968 + 0x0013a> in <filename unknown>:0
Edit 2:
After digging deeper into the issue, we traced it down to these lines in Script.cs
:
/// <summary>
/// Gets the references that need to be assigned to the compilation.
/// This can be different than the list of references defined by the <see cref="ScriptOptions"/> instance.
/// </summary>
internal ImmutableArray<MetadataReference> GetReferencesForCompilation(
CommonMessageProvider messageProvider,
DiagnosticBag diagnostics,
MetadataReference languageRuntimeReferenceOpt = null)
{
var resolver = Options.MetadataResolver;
var references = ArrayBuilder<MetadataReference>.GetInstance();
try
{
if (Previous == null)
{
var corLib = MetadataReference.CreateFromAssemblyInternal(typeof(object).GetTypeInfo().Assembly);
references.Add(corLib);
No matter what options we pass, this will always be called on compilation, MetadataReference.CreateFromAssemblyInternal
tries to load a file from disk. So it appears that loading assemblies from disk is hardcoded into the process. We're looking for a clean way of overriding this.
We were already successful in loading assemblies from streams using HttpClient:
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var name = assembly.GetName().Name + ".dll";
references.Add(
MetadataReference.CreateFromStream(
await this.HttpClient.GetStreamAsync("/_framework/_bin/" + name)));
}
But this doesn't matter as long as CSharpScript is loading another set of assemblies during compilation from disk
The
CsharpScript
API unfortunately has been built with a dependency on looking up assemblies on the local filesystem. If you dig deep enough in theMetadataReference.CreateFromStream
source, you will see the culprit being a call toFile.OpenRead()
I spent some time myself looking into any possible extensibility of the API where one could supply an alternative assembly resolution strategy. There is a path via the supply of a custom
MetaDataReferenceResolver
in theScriptOptions
however the baking in of the line you discoveredeffectively creates the hard dependency on the file system which kills the idea.
The resolution of
mscorlib
is done this way above, but then all other asemblies use theOptions.MetaDataReferences
list. Why I have no idea....As a pure hack I even had a crack at capturing the call to
File.OpenRead()
and returing my ownstream
fromHttpClient
. The injection works in a desktop process, but something was going wrong in wasm. In the end I gave up too in favour of rolling an alternative such as this approach or this one.Here was the trial hack for anyone who is interested.
HTH