【Azure Developer】使用Azure PubSub服務示例程式碼時候遇見了.NET 6.0的程式碼轉換問題
- 2022 年 5 月 18 日
- 筆記
- 【Azure Developer】, Azure Developer, 【Azure 環境】, .NET 5 轉移到.NET 6, CS8803, Top-level statements must precede, namespace and type declarations
問題描述
當本地環境中安裝.NET 6.0後,用指令 dotnet new web 或 dotnet new console 生成的項目,使用的都是新模板生成的Program.cs文件。裡面去掉了namespace, class 以及main函數的定義。使得程式碼更簡潔。
生成的 Program.cs 程式碼為:
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run();
與示例程式碼中所使用的程式碼結構差距十分巨大(示例鏈接://docs.microsoft.com/en-us/azure/azure-web-pubsub/tutorial-subprotocol?tabs=csharp):
using Azure.Messaging.WebPubSub; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Azure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace logstream { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddAzureClients(builder => { builder.AddWebPubSubServiceClient(Configuration["Azure:WebPubSub:ConnectionString"], "stream"); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/negotiate", async context => { var service = context.RequestServices.GetRequiredService<WebPubSubServiceClient>(); var response = new { url = service.GetClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" }).AbsoluteUri }; await context.Response.WriteAsJsonAsync(response); }); }); } } }
那麼如何來定義 ConfigureServices, 如何來 Configure 中的程式碼呢?如何來解決 CS8803 : Top-level statements must precede namespace and type declarations

問題分析
這是程式碼由.NET 5.0 到 .NET 6.0的升級轉換問題。
在.NET 6 之前,每一個應用程式都將應用的初始化程式碼拆分放在 Program.cs 和 Startup.cs 文件中。他們是兩個獨立的類,而在.NET 6.0中,為了簡潔,為了最小化API,把兩個類進行了合併。生成新的Program.cs中去掉了命名空間(Namespace, Class定義, Main函數)。使得在啟動一個應用時,程式碼達到最少。 (PS:C# 編譯器會自動生成Mian函數)
在查看官方對於5.0 變為 6.0的文檔介紹://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60?view=aspnetcore-6.0&tabs=visual-studio
- ConfigureServices 函數裡面的 services.AddXXXXX()等都可以轉換為 builder.Services.AddXXXXX()
- Configure 函數中的內容,可以直接寫在 var app = builder.Build(); 程式碼之後。
所以,本文之前的程式碼,可以直接轉換為:
using Azure.Messaging.WebPubSub; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Azure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; var ConnectionString = ""; var builder = WebApplication.CreateBuilder(args); builder.Services.AddAzureClients(builder => { builder.AddWebPubSubServiceClient(ConnectionString, "stream"); }); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/negotiate", async context => { var service = context.RequestServices.GetRequiredService<WebPubSubServiceClient>(); var response = new { url = service.GetClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" }).AbsoluteUri }; await context.Response.WriteAsJsonAsync(response); }); }); app.MapGet("/", () => "Hello World!"); app.Run();

特別注意,在新模板下的程式碼,非常容易出現:CS8803 : Top-level statements must precede namespace and type declarations 錯誤。這是因為 .NET 6.0在隱藏了Main函數後,在編譯程式碼時候,會自動補上Main函數,所以在Program.cs 的程式碼中,如果需要定義其他類,必須放在文件的末尾,不能在文件開頭部分和中間。
當Class定義程式碼放在Program.cs開頭部分

當Class定義程式碼放在Program.cs中間部分
當Class定義程式碼放在Program.cs結尾部分

更多關於Top-level statements的說明,請見://www.cnblogs.com/lulight/articles/16285885.html


