我正在尝试编写一个 Blazor WebAssembly (WASM) 应用程序,它接受一些代码(来自某些文本输入字段)并使用 Roslyn 编译代码。
我使用 Roslyn 的 CSharpCompilation
类来创建编译。它的 Create 方法采用四个参数,其中之一是 MetadataReference(也称为程序集引用)列表。在其他(非 blazor)类型的应用程序中,例如 C# 控制台应用程序,您可以根据程序集位置获取这些 MetadataReference
,如下所示:
var locatedAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => !string.IsNullOrEmpty(a.Location)).ToArray();
foreach (var assembly in locatedAssemblies)
{
MetadataReference reference = MetadataReference.CreateFromFile(assembly.Location);
}
不幸的是,这在 Blazor WASM 中不再有效,因为程序集的 Location
为空。
我曾尝试以不同的方式获取程序集,例如 AppDomain.CurrentDomain.GetAssemblies()
和 Assembly.GetEntryAssembly().GetReferencedAssemblies()
,但全部都是空的 位置
。我也尝试调用 Assembly.Load()
,但无济于事。
有谁知道如何在 Blazor WASM 中获取MetadataReference
,或者我如何在 Blazor WASM 中创建编译?
(我还知道我可能需要使用 MetadataReference.CreateFromStream()
,但它仍然需要程序集位置)。
提前致谢。
最佳答案
我还想在 Blazor WASM 应用程序中编译 C#,但发现您的问题没有答案。经过一番挖掘,我能够创建一个工作演示(下面的存储库链接)。基本上使用 HttpClient 获取每个程序集的字节并使用 MetadataReference.CreateFromImage(bytes)。
我创建的完整基本示例存储库:https://github.com/LostBeard/BlazorWASMScriptLoader
ScriptLoaderService.cs 来源:
using Microsoft.AspNetCore.Components;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Immutable;
using System.Reflection;
namespace BlazorWASMScriptLoader
{
// requires "Microsoft.CodeAnalysis.CSharp"
// can be added via nuget
public class ScriptLoaderService
{
HttpClient _httpClient = new HttpClient();
public ScriptLoaderService(NavigationManager navigationManager)
{
_httpClient.BaseAddress = new Uri(navigationManager.BaseUri);
}
async Task<MetadataReference?> GetAssemblyMetadataReference(Assembly assembly)
{
MetadataReference? ret = null;
var assmeblyName = assembly.GetName().Name;
var assemblyUrl = $"./_framework/{assmeblyName}.dll";
try
{
var tmp = await _httpClient.GetAsync(assemblyUrl);
if (tmp.IsSuccessStatusCode)
{
var bytes = await tmp.Content.ReadAsByteArrayAsync();
ret = MetadataReference.CreateFromImage(bytes);
}
}
catch { }
return ret;
}
public async Task<Assembly?> CompileToDLLAssembly(string sourceCode, string assemblyName = "")
{
if (string.IsNullOrEmpty(assemblyName)) assemblyName = Path.GetRandomFileName();
var codeString = SourceText.From(sourceCode);
var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp11);
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
var appAssemblies = Assembly.GetEntryAssembly()?.GetReferencedAssemblies().Select(o => Assembly.Load(o)).ToList();
appAssemblies.Add(typeof(object).Assembly);
var references = new List<MetadataReference>();
foreach (var assembly in appAssemblies)
{
var metadataReference = await GetAssemblyMetadataReference(assembly);
if (metadataReference == null)
{
// assembly may be located elsewhere ... handle if needed
continue;
}
var metadataReferene = metadataReference;
references.Add(metadataReferene);
}
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { parsedSyntaxTree },
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
concurrentBuild: false,
optimizationLevel: OptimizationLevel.Debug
)
);
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return null;
}
else
{
ms.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray());
return assembly;
}
}
}
}
}
关于.net-core - 如何在 Blazor WebAssembly 中使用 Roslyn 创建 CSharpCompilation?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74351536/