在Blazor wasm中使用CSharpScript时出现System.IO.FileNotFoundException

时间:2020-05-10 11:22:15

标签: c# .net-core blazor blazor-client-side csharpscript

我正在尝试在Blazor wasm应用程序中使用CSharpScript,并通过简单的EvaluateAsync进行测试:

var result = await CSharpScript.EvaluateAsync<int>("1 + 1");

抛出:System.IO.FileNotFoundException: Could not find file "/mscorlib.dll"

我正在使用Blazor wasm 3.2.0-preview3.20168.3

修改: 这是index.razor中的完整代码:

@code{
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        Console.WriteLine("Initializing...");
        var result = await CSharpScript.EvaluateAsync<int>("1 + 1");
    }
}

这是控制台输出:

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

编辑2: 在深入研究问题之后,我们在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);

无论我们通过什么选项,都将始终在编译时调用它,MetadataReference.CreateFromAssemblyInternal尝试从磁盘加载文件。因此看来,从磁盘加载程序集已被硬编码到进程中。我们正在寻找一种替代此方法的干净方法。

我们已经成功使用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)));
}

但这无关紧要,只要CSharpScript在从磁盘进行编译期间正在加载另一组程序集

2 个答案:

答案 0 :(得分:3)

恐怕会引发此异常,因为您正在尝试访问客户端磁盘上的文件。 JavaScript不允许这样做,因此Blazor WebAssembly也不允许这样做。

此外,还在磁盘上搜索文件,但在Blazor中,程序集未存储在磁盘上...

希望这对您有帮助...

答案 1 :(得分:2)

不幸的是,CsharpScript API的构建依赖于在本地文件系统上查找程序集。如果您在MetadataReference.CreateFromStream来源中进行了足够的深入研究,您会发现罪魁祸首是对File.OpenRead()的呼唤

我花了一些时间自己研究API的任何可能的扩展,以便可以提供一种替代的程序集解析策略。在MetaDataReferenceResolver中有一个通过提供自定义ScriptOptions的路径,但是您发现的行的引入

 var corLib = MetadataReference.CreateFromAssemblyInternal(typeof(object).GetTypeInfo().Assembly);

有效地建立了对文件系统的硬性依赖,从而扼杀了这个主意。

mscorlib的解析是通过上面的这种方式完成的,但是随后所有其他程序集都使用Options.MetaDataReferences列表。为什么我不知道......

作为一个纯粹的技巧,我什至在捕获对File.OpenRead()的调用并从stream重新获得自己的HttpClient的过程中也遇到了困难。注入是在桌面过程中进行的,但是wasm中出了点问题。最后,我也放弃了使用this approachthis one之类的替代项。

这里是有兴趣的人的试用版。

    //Using the Pose library https://github.com/tonerdo/pose
    var client = new HttpClient() { BaseAddress = new Uri(navigationManager.BaseUri) };
    Shim fileShim = Shim.Replace(() => System.IO.File.OpenRead(Is.A<string>())).With(delegate (string assembly)
    {
        var fs = new System.IO.FileStream(assembly, System.IO.FileMode.Open);
        client.GetStreamAsync($"_framework/_bin/{assembly}").Result.CopyTo(fs);
        return fs;
    });

    PoseContext.Isolate(() => Text = CSharpScript.EvaluateAsync<string>("hello").Result, fileShim);

HTH