【Azure Developer】使用Azure PubSub服務示例程式碼時候遇見了.NET 6.0的程式碼轉換問題

路邊兩盞燈 發表於 2022-05-18
.Net

問題描述

當本地環境中安裝.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();

與示例程式碼中所使用的程式碼結構差距十分巨大(示例連結:https://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

 【Azure Developer】使用Azure PubSub服務示例程式碼時候遇見了.NET 6.0的程式碼轉換問題

問題分析

這是程式碼由.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的文件介紹:https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60?view=aspnetcore-6.0&tabs=visual-studio

  1. ConfigureServices 函式裡面的 services.AddXXXXX()等都可以轉換為  builder.Services.AddXXXXX()
  2. 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();

【Azure Developer】使用Azure PubSub服務示例程式碼時候遇見了.NET 6.0的程式碼轉換問題

 

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

當Class定義程式碼放在Program.cs開頭部分

【Azure Developer】使用Azure PubSub服務示例程式碼時候遇見了.NET 6.0的程式碼轉換問題

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

【Azure Developer】使用Azure PubSub服務示例程式碼時候遇見了.NET 6.0的程式碼轉換問題

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

【Azure Developer】使用Azure PubSub服務示例程式碼時候遇見了.NET 6.0的程式碼轉換問題

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

 

參考資料

Tutorial: Publish and subscribe messages between WebSocket clients using subprotocol: https://docs.microsoft.com/en-us/azure/azure-web-pubsub/tutorial-subprotocol?tabs=csharp Migrate from ASP.NET Core 5.0 to 6.0:https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60?view=aspnetcore-6.0&tabs=visual-studio