Kompilasi dan jalankan C # dan Blazor di dalam browser

Pendahuluan



Jika Anda adalah pengembang web dan sedang mengembangkan untuk browser, maka Anda pasti sudah familiar dengan JS, yang dapat berjalan di dalam browser. Ada pendapat bahwa JS tidak cocok untuk perhitungan dan algoritma yang rumit. Dan meskipun dalam beberapa tahun terakhir JS telah membuat lompatan besar dalam kinerja dan luasnya penggunaan, banyak programmer terus bermimpi meluncurkan bahasa sistem di dalam browser. Dalam waktu dekat, game mungkin berubah karena WebAssembly.


Microsoft tidak berdiri diam dan secara aktif mencoba untuk porting .NET ke WebAssembly. Sebagai salah satu hasilnya, kami mendapat kerangka kerja baru untuk pengembangan klien - Blazor. Belum jelas apakah Blazor bisa lebih cepat dari kerangka JS modern seperti React, Angular, Vue karena WebAssembly. Tapi itu pasti memiliki keuntungan besar - pengembangan di C #, serta seluruh dunia .NET Core dapat digunakan di dalam aplikasi.


Kompilasi dan eksekusi C # di Blazor


Proses mengkompilasi dan mengeksekusi bahasa yang kompleks seperti C # adalah tugas yang kompleks dan memakan waktu. #? - Tergantung pada kemampuan teknologi (atau lebih tepatnya, inti). Namun, Microsoft, ternyata, sudah menyiapkan segalanya untuk kami.


Pertama, buat aplikasi Blazor.



Setelah itu, Anda perlu menginstal Nuget - paket untuk menganalisis dan mengkompilasi C #.


 Install-Package Microsoft.CodeAnalysis.CSharp 

Persiapkan halaman awal.


 @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(); } } 

Pertama, Anda perlu mengurai string menjadi pohon sintaksis abstrak. Karena pada langkah selanjutnya kita akan mengkompilasi komponen Blazor - kita memerlukan versi bahasa terbaru ( LanguageVersion.Latest ). Ada metode untuk Roslyn untuk C # untuk ini:


 SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Latest)); 

Sudah pada tahap ini, Anda dapat mendeteksi kesalahan kompilasi kotor dengan membaca diagnostik parser.


  foreach (var diagnostic in syntaxTree.GetDiagnostics()) { CompileLog.Add(diagnostic.ToString()); } 

Selanjutnya, kita mengkompilasi Assembly menjadi aliran biner.


 CSharpCompilation compilation = CSharpCompilation.Create("CompileBlazorInBlazor.Demo", new[] {syntaxTree}, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (MemoryStream stream = new MemoryStream()) { EmitResult result = compilation.Emit(stream); } 

Perlu dicatat bahwa Anda perlu mendapatkan references - daftar metadata perpustakaan yang terhubung. Tapi saya tidak bisa membaca file-file ini di sepanjang jalur Assembly.Location , karena tidak ada sistem file di browser. Mungkin ada cara yang lebih efisien untuk menyelesaikan masalah ini, tetapi tujuan dari artikel ini adalah kesempatan konseptual, jadi kami akan mengunduh pustaka ini lagi melalui Http dan melakukan ini hanya pada awal pertama kompilasi.


 foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { references.Add( MetadataReference.CreateFromStream( await this._http.GetStreamAsync("/_framework/_bin/" + assembly.Location))); } 

Dari EmitResult Anda dapat mengetahui apakah kompilasi berhasil, serta mendapatkan kesalahan diagnostik.
Sekarang Anda perlu memuat Assembly ke dalam AppDomain saat ini dan menjalankan kode yang dikompilasi. Sayangnya, tidak ada cara untuk membuat beberapa AppDomain di dalam browser, jadi itu tidak akan berfungsi untuk memuat dan membongkar Assembly dengan aman.


 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}); 


Pada tahap ini, kami mengkompilasi dan mengeksekusi kode C # langsung di browser. Suatu program dapat terdiri dari beberapa file dan menggunakan pustaka .NET lainnya. Bukankah itu hebat? Sekarang mari kita lanjutkan.


Kompilasi dan jalankan komponen Blazor di browser.


Komponen Blazor adalah Razor dimodifikasi Razor . Oleh karena itu, untuk mengkompilasi komponen Blazor, Anda perlu menggunakan seluruh lingkungan untuk mengkompilasi template Razor dan mengkonfigurasi ekstensi untuk Blazor. Anda perlu menginstal paket Microsoft.AspNetCore.Blazor.Build dari nuget. Namun, menambahkannya ke proyek Blazor kami akan gagal, karena penghubung tidak akan dapat mengkompilasi proyek. Karena itu, Anda perlu mengunduhnya, dan kemudian secara manual menambahkan 3 perpustakaan.


 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 

Mari kita buat kernel untuk mengkompilasi Razor dan memodifikasinya untuk Blazor, karena secara default kernel akan menghasilkan kode Razor untuk halaman-halamannya.


 var engine = RazorProjectEngine.Create(BlazorExtensionInitializer.DefaultConfiguration, fileSystem, b => { BlazorExtensionInitializer.Register(b); }); 

Untuk eksekusi, hanya fileSystem yang fileSystem - ini adalah abstraksi dari sistem file. Kami menerapkan sistem file kosong, namun, jika Anda ingin mengkompilasi proyek yang kompleks dengan dukungan untuk _ViewImports.cshtml , maka Anda perlu menerapkan struktur yang lebih kompleks dalam memori.
Sekarang kita akan menghasilkan kode dari komponen Blazor dari kode C #.


  var file = new MemoryRazorProjectItem(code); var doc = engine.Process(file).GetCSharpDocument(); var csCode = doc.GeneratedCode; 

Anda juga dapat menerima pesan diagnostik dari doc tentang hasil menghasilkan kode C # dari komponen Blazor.
Sekarang kami mendapat kode komponen C #. Anda perlu SyntaxTree , lalu mengkompilasi Majelis, memuatnya ke AppDomain saat ini dan menemukan jenis komponen. Sama seperti pada contoh sebelumnya.


Masih memuat komponen ini ke dalam aplikasi saat ini. Ada beberapa cara untuk melakukan ini, misalnya, dengan membuat RenderFragment Anda sendiri.


 @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; } } } 


Kesimpulan


Kami mengkompilasi dan meluncurkan komponen di browser Blazor. Jelas, kompilasi penuh kode C # dinamis tepat di dalam browser dapat mengesankan setiap programmer.


Tetapi di sini Anda harus mempertimbangkan "perangkap" seperti itu:


  • Ekstensi dan pustaka bind tambahan diperlukan untuk mendukung bind dua arah mengikat.
  • Untuk mendukung async, await , kami juga menghubungkan tambahan. perpustakaan
  • Mengkompilasi komponen terkait Blazor membutuhkan kompilasi dua langkah.

Semua masalah ini telah diselesaikan dan ini merupakan topik untuk artikel terpisah.


Git


Demo

Source: https://habr.com/ru/post/id433818/


All Articles