Einführung

Wenn Sie ein Webentwickler sind und für einen Browser entwickeln, sind Sie definitiv mit JS vertraut, das im Browser ausgeführt werden kann. Es gibt eine Meinung, dass JS für komplexe Berechnungen und Algorithmen nicht sehr geeignet ist. Und obwohl JS in den letzten Jahren einen großen Sprung in Leistung und Anwendungsbreite gemacht hat, träumen viele Programmierer weiterhin davon, die Systemsprache im Browser zu starten. In naher Zukunft kann sich das Spiel aufgrund von WebAssembly ändern.
Microsoft steht nicht still und versucht aktiv, .NET auf WebAssembly zu portieren. Als eines der Ergebnisse haben wir ein neues Framework für die Kundenentwicklung erhalten - Blazor. Es ist noch nicht klar, ob Blazor aufgrund von WebAssembly schneller sein kann als moderne JS-Frameworks wie React, Angular, Vue. Aber es hat definitiv einen großen Vorteil - die Entwicklung in C # sowie die gesamte Welt von .NET Core können innerhalb der Anwendung verwendet werden.
Kompilieren und Ausführen von C # in Blazor
Das Kompilieren und Ausführen einer so komplexen Sprache wie C # ist eine komplexe und zeitaufwändige Aufgabe. #?
- Es hängt von den Fähigkeiten der Technologie (oder vielmehr vom Kern) ab. Wie sich herausstellte, hatte Microsoft jedoch bereits alles für uns vorbereitet.
Erstellen Sie zunächst eine Blazor-Anwendung.

Danach müssen Sie Nuget installieren - ein Paket zum Analysieren und Kompilieren von C #.
Install-Package Microsoft.CodeAnalysis.CSharp
Bereiten Sie die Startseite vor.
@page "/" @inject CompileService service <h1>Compile and Run C# in Browser</h1> <div> <div class="form-group"> <label for="exampleFormControlTextarea1">C# Code</label> <textarea class="form-control" id="exampleFormControlTextarea1" rows="10" bind="@CsCode"></textarea> </div> <button type="button" class="btn btn-primary" onclick="@Run">Run</button> <div class="card"> <div class="card-body"> <pre>@ResultText</pre> </div> </div> <div class="card"> <div class="card-body"> <pre>@CompileText</pre> </div> </div> </div> @functions { string CsCode { get; set; } string ResultText { get; set; } string CompileText { get; set; } public async Task Run() { ResultText = await service.CompileAndRun(CsCode); CompileText = string.Join("\r\n", service.CompileLog); this.StateHasChanged(); } }
Zuerst müssen Sie die Zeichenfolge in einen abstrakten Syntaxbaum analysieren. Da wir im nächsten Schritt Blazor-Komponenten kompilieren werden, benötigen wir die neueste ( LanguageVersion.Latest
) Version der Sprache. Hierfür gibt es eine Methode für Roslyn für C #:
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Latest));
Bereits zu diesem Zeitpunkt können Sie grobe Kompilierungsfehler erkennen, indem Sie die Parser-Diagnose lesen.
foreach (var diagnostic in syntaxTree.GetDiagnostics()) { CompileLog.Add(diagnostic.ToString()); }
Als nächstes kompilieren wir Assembly
zu einem binären Stream.
CSharpCompilation compilation = CSharpCompilation.Create("CompileBlazorInBlazor.Demo", new[] {syntaxTree}, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (MemoryStream stream = new MemoryStream()) { EmitResult result = compilation.Emit(stream); }
Es ist zu beachten, dass Sie references
benötigen - eine Liste von Metadaten verbundener Bibliotheken. Ich konnte diese Dateien jedoch nicht im Assembly.Location
Pfad lesen, da der Browser kein Dateisystem enthält. Es gibt möglicherweise einen effizienteren Weg, um dieses Problem zu lösen, aber der Zweck dieses Artikels ist eine konzeptionelle Möglichkeit. Daher werden wir diese Bibliotheken erneut über HTTP herunterladen und dies erst beim ersten Start der Kompilierung tun.
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { references.Add( MetadataReference.CreateFromStream( await this._http.GetStreamAsync("/_framework/_bin/" + assembly.Location))); }
In EmitResult
Sie herausfinden, ob die Kompilierung erfolgreich war, und Diagnosefehler erhalten.
Jetzt müssen Sie Assembly
in die aktuelle AppDomain
und den kompilierten Code ausführen. Leider gibt es keine Möglichkeit, mehrere AppDomain
im Browser zu erstellen, sodass das sichere Laden und Entladen von Assembly
nicht funktioniert.
Assembly assemby = AppDomain.CurrentDomain.Load(stream.ToArray()); var type = assemby.GetExportedTypes().FirstOrDefault(); var methodInfo = type.GetMethod("Run"); var instance = Activator.CreateInstance(type); return (string) methodInfo.Invoke(instance, new object[] {"my UserName", 12});

Zu diesem Zeitpunkt haben wir C # -Code direkt im Browser kompiliert und ausgeführt. Ein Programm kann aus mehreren Dateien bestehen und andere .NET-Bibliotheken verwenden. Ist das nicht toll? Nun gehen wir weiter.
Kompilieren Sie die Blazor-Komponente und führen Sie sie in einem Browser aus.
Blazor-Komponenten sind Razor
Vorlagen. Um die Blazor-Komponente zu kompilieren, müssen Sie daher eine gesamte Umgebung bereitstellen, um Razor-Vorlagen zu kompilieren und Erweiterungen für Blazor zu konfigurieren. Sie müssen das Microsoft.AspNetCore.Blazor.Build
Paket von Nuget installieren. Das Hinzufügen zu unserem Blazor-Projekt schlägt jedoch fehl, da der Linker das Projekt dann nicht kompilieren kann. Daher müssen Sie es herunterladen und dann manuell 3 Bibliotheken hinzufügen.
microsoft.aspnetcore.blazor.build\0.7.0\tools\Microsoft.AspNetCore.Blazor.Razor.Extensions.dll microsoft.aspnetcore.blazor.build\0.7.0\tools\Microsoft.AspNetCore.Razor.Language.dll microsoft.aspnetcore.blazor.build\0.7.0\tools\Microsoft.CodeAnalysis.Razor.dll
Erstellen wir einen Kernel zum Kompilieren von Razor
und ändern ihn für Blazor, da der Kernel standardmäßig Razor-Code für die Seiten generiert.
var engine = RazorProjectEngine.Create(BlazorExtensionInitializer.DefaultConfiguration, fileSystem, b => { BlazorExtensionInitializer.Register(b); });
Zur Ausführung fileSystem
nur das fileSystem
- dies ist eine Abstraktion über das Dateisystem. Wir haben ein leeres Dateisystem implementiert. Wenn Sie jedoch komplexe Projekte mit Unterstützung für _ViewImports.cshtml
kompilieren _ViewImports.cshtml
, müssen Sie eine komplexere Struktur im Speicher implementieren.
Jetzt generieren wir den Code aus der Blazor-Komponente des C # -Codes.
var file = new MemoryRazorProjectItem(code); var doc = engine.Process(file).GetCSharpDocument(); var csCode = doc.GeneratedCode;
Sie können vom doc
auch Diagnosemeldungen über die Ergebnisse der Generierung von C # -Code aus der Blazor-Komponente erhalten.
Jetzt haben wir den Code der C # -Komponente. Sie müssen SyntaxTree
, dann Assembly kompilieren, in die aktuelle AppDomain laden und den Komponententyp ermitteln. Gleich wie im vorherigen Beispiel.
Diese Komponente muss noch in die aktuelle Anwendung geladen werden. Es gibt verschiedene Möglichkeiten, dies zu tun, indem Sie beispielsweise Ihr eigenes RenderFragment
.
@inject CompileService service <div class="card"> <div class="card-body"> @Result </div> </div> @functions { RenderFragment Result = null; string Code { get; set; } public async Task Run() { var type = await service.CompileBlazor(Code); if (type != null) { Result = builder => { builder.OpenComponent(0, type); builder.CloseComponent(); }; } else { Result = null; } } }

Fazit
Wir haben die Komponente im Blazor-Browser kompiliert und gestartet. Offensichtlich kann eine vollständige Zusammenstellung von dynamischem C # -Code direkt im Browser jeden Programmierer beeindrucken.
Aber hier sollten Sie solche "Fallstricke" berücksichtigen:
- Zusätzliche
bind
und Bibliotheken sind erforderlich, um bind
bidirektionale Bindungsbindung zu unterstützen. async, await
zu unterstützen async, await
und verbinden Sie sich zusätzlich. Bibliotheken- Das Kompilieren von Blazor-bezogenen Komponenten erfordert eine zweistufige Kompilierung.
Alle diese Probleme wurden bereits behoben und dies ist ein Thema für einen separaten Artikel.
Git
Demo