共用方式為


將 ASP.NET 架構工作階段移轉至 ASP.NET 核心

會話狀態是許多 Web 應用程式的關鍵元件,跨 HTTP 請求儲存使用者特定的資料。 從 ASP.NET Framework 遷移到 ASP.NET Core 時,會話狀態會帶來獨特的挑戰,因為這兩個框架處理會話的方式非常不同。

為什麼工作階段移轉很複雜

ASP.NET Framework 和 ASP.NET Core 對會話管理有著根本不同的方法:

  • ASP.NET Framework 提供自動物件序列化和內建會話鎖定
  • ASP.NET Core 需要手動序列化,並且不提供會話鎖定保證

這些差異表示您無法簡單地將工作階段程式碼從 Framework 移至 Core 而不進行變更。

移轉策略概觀

在移轉期間,您有三種主要方法來處理會話狀態:

  1. 完全重寫 - 重寫所有會話程式碼以使用 ASP.NET Core 的原生會話實作
  2. 使用個別會話的增量 - 逐步移轉組件,讓每個應用程式維護自己的會話狀態
  3. 增量式且共用工作階段 - 在框架和核心應用程式之間共享工作階段資料時遷移元件

對於大部分的應用程式,移轉至 ASP.NET 核心工作階段 可提供最佳效能和可維護性。 不過,較大的應用程式或具有複雜會話需求的應用程式可能會受益於使用 System.Web 配接器的累加式方法。

選擇移轉方法

您有三個主要選項可將工作階段狀態從 ASP.NET Framework 移轉至 ASP.NET Core。 您的選擇取決於您的移轉時間表、您是否需要同時執行兩個應用程式,以及您願意重寫多少程式碼。

快速決策指南

回答以下問題以選擇您的方法:

  1. 您是否正在進行完全重寫或增量遷移?

  2. 您的 ASP.NET Framework 和 ASP.NET Core 應用程式是否需要存取相同的工作階段資料?

了解差異

在深入了解實作細節之前,請務必瞭解 ASP.NET Framework 和 ASP.NET Core 如何以不同的方式處理會話狀態:

  • 物件序列化
    • ASP.NET Framework 會自動序列化和反序列化物件(除非使用記憶體儲存)
    • ASP.NET Core 需要手動序列化/還原序列化,並將資料儲存為 byte[]
  • 會話鎖定
    • ASP.NET Framework 鎖定 Session 內的使用情況,並依序處理後續請求
    • ASP.NET Core 不提供會話鎖定保證

移轉方法比較

方法 程式碼變更 績效 會話分享 使用時機
內建 ASP.NET 核心 高 - 重寫所有會話程式碼 最佳 沒有 全面重寫、效能為關鍵的應用程式
包裝 ASP.NET Core 低 - 保留現有的會話模式 沒有 增量遷移,不需要共用狀態
遠端應用程式 低 - 保留現有的會話模式 普通 完整 同時運行兩個應用程序

System.Web 配接器透過兩個關鍵介面橋接 ASP.NET Framework 和 Core 會話實作之間的差異,來啟用「Wrapped」與「Remote app」的方法。

  • Microsoft.AspNetCore.SystemWebAdapters.ISessionManager:接受 HttpContext 和 session 的中繼資料,並傳回 ISessionState 物件
  • Microsoft.AspNetCore.SystemWebAdapters.ISessionState:描述會話物件狀態並支援 HttpSessionState 類型

內建 ASP.NET 核心會話狀態

當您執行完整的移轉,並可以重寫工作階段相關程式碼以使用 ASP.NET Core 的原生工作階段實作時,請選擇此方法。

ASP.NET Core 提供輕量型、高效能的工作階段狀態實作,可將資料儲存為 byte[] 並需要明確序列化。 這種方法提供了最佳效能,但在遷移過程中需要進行更多程式碼變更。

如需如何設定和使用它的詳細資訊,請參閱 [ASP.NET 工作階段文件]((xref:fundamentals/app-state.md)。

優缺點

優點 缺點
最佳效能和最低記憶體佔用 需要重寫所有會話存取程式碼
原生 ASP.NET Core 實作 沒有自動物件序列化
完全控制序列化策略 沒有會話鎖定(並發請求可能會衝突)
沒有額外的相依性 ASP.NET 架構模式的重大變更
支援所有 ASP.NET 核心會話提供者 會話金鑰區分大小寫 (與 Framework 不同)

移轉考慮

移轉至內建 ASP.NET Core 工作階段時:

需要更改代碼:

  • Session["key"] 取代為 HttpContext.Session.GetString("key")
  • Session["key"] = value 取代為 HttpContext.Session.SetString("key", value)
  • 新增複雜物件的明確序列化/還原序列化
  • 明確處理 Null 值 (沒有自動類型轉換)

資料遷移:

  • 會話資料結構變更需要仔細規劃
  • 請考慮在移轉期間平行執行這兩個系統
  • 如有需要,實作工作階段資料匯入/匯出工具

測試策略:

  • 單元測試工作階段序列化/還原序列化邏輯
  • 跨要求的整合測試工作階段行為
  • 負載測試並行工作階段存取模式

何時選擇此方法:

  • 您可以負擔重寫與會話相關的程式碼
  • 性能是重中之重
  • 您未與舊版應用程式共用會話資料
  • 您想要完全消除 System.Web 相依性

System.Web 配接器會話

備註

這會利用 System.Web 配接器 來簡化移轉。

序列化組態

物件需要序列化遠端應用程式工作階段狀態。

在 ASP.NET 框架中, BinaryFormatter 用於自動序列化會話值內容。 要將這些序列化以便與 System.Web 配接器搭配使用,必須透過 ISessionKeySerializer 實作來明確設定序列化。

開箱即用,有一個簡單的 JSON 序列化器,允許使用 JsonSessionSerializerOptions 將每個會話金鑰註冊到已知類型。

  • RegisterKey<T>(string) - 向已知型別註冊會話金鑰。 正確的序列化/還原序列化需要此註冊。 缺少註冊導致錯誤並阻止會話存取。
builder.Services.AddSystemWebAdapters()
    .AddJsonSessionSerializer(options =>
    {
        // Serialization/deserialization requires each session key to be registered to a type
        options.RegisterKey<int>("test-value");
    });

如果需要更多自定義,則 ISessionKeySerializer 可以實施:

builder.Services.AddSystemWebAdapters()
    .AddSessionSerializer();

builder.Services.AddSingleton<ISessionKeySerializer, CustomSessionKeySerializer>();
sealed class CustomSessionKeySerializer : ISessionKeySerializer
{
    public bool TryDeserialize(string key, byte[] bytes, out object? obj)
    {
        // Custom deserialization logic
    }

    public bool TrySerialize(string key, object? value, out byte[] bytes)
    {
        // Custom serialization logic
    }
}

備註

使用 AddJsonSessionSerializer 註冊模式時,無需調用 AddSessionSerializer ,因為它會自動添加。 如果您只想使用自訂實作,則必須手動新增它。

啟用工作階段

會話支援需要明確啟用。 在 ASP.NET Core 中,您可以使用中繼資料逐路由或全域設定:

  • 標註控制器
[Session]
public class SomeController : Controller
{
}
  • 為所有端點全域啟用
app.MapDefaultControllerRoute()
    .RequireSystemWebAdapterSession();

已包裝 ASP.NET 核心會話狀態

當您移轉的元件不需要與舊版應用程式共用工作階段資料時,請選擇此方法。

擴充方法會新增包裝的 ASP.NET Core 會話,以與配接器協作。 它使用與 ISession 相同的後備存放區,但提供強型別的存取功能。

ASP.NET Core 的配置:

builder.Services.AddSystemWebAdapters()
    .AddJsonSessionSerializer(options =>
    {
        // Serialization/deserialization requires each session key to be registered to a type
        options.RegisterKey<int>("test-value");
        options.RegisterKey<SessionDemoModel>("SampleSessionItem");
    })
    .AddWrappedAspNetCoreSession();

您的架構應用程式不需要變更。

如需詳細資訊,請參閱 封裝的 Session State 範例應用程式

遠端應用程式會話狀態

備註

這會利用 System.Web 配接器 來簡化移轉。

當您需要在增量移轉期間在 ASP.NET Framework 和 ASP.NET Core 應用程式之間共用工作階段狀態時,請選擇此方法。

遠端應用程式工作階段可讓應用程式之間的通訊,藉由在 ASP.NET Framework 應用程式上公開端點來擷取和設定工作階段狀態。

先決條件

完成 遠端應用程式設定 指示,以連接 ASP.NET Core 和 ASP.NET Framework 應用程式。

應用程式設定

ASP.NET 核心配置:

呼叫 AddRemoteAppSessionAddJsonSessionSerializer 註冊已知的工作階段項目類型:

builder.Services.AddSystemWebAdapters()
    .AddJsonSessionSerializer(options =>
    {
        // Serialization/deserialization requires each session key to be registered to a type
        options.RegisterKey<int>("test-value");
        options.RegisterKey<SessionDemoModel>("SampleSessionItem");
    })
    .AddRemoteAppClient(options =>
    {
        // Provide the URL for the remote app that has enabled session querying
        options.RemoteAppUrl = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);

        // Provide a strong API key that will be used to authenticate the request on the remote app for querying the session
        options.ApiKey = builder.Configuration["RemoteAppApiKey"];
    })
    .AddSessionClient();

ASP.NET 框架配置:

將此變更新增至 Global.asax.cs

public class Global : HttpApplication
{
        protected void Application_Start()
        {
            HttpApplicationHost.RegisterHost(builder =>
            {
                builder.AddSystemWebAdapters()
                    .AddJsonSessionSerializer(options =>
                    {
                        // Serialization/deserialization requires each session key to be registered to a type
                        options.RegisterKey<int>("test-value");
                        options.RegisterKey<SessionDemoModel>("SampleSessionItem");
                    })
                    // Provide a strong API key that will be used to authenticate the request on the remote app for querying the session
                    // ApiKey is a string representing a GUID
                    .AddRemoteAppServer(options => options.ApiKey = ConfigurationManager.AppSettings["RemoteAppApiKey"])
                    .AddSessionServer();
            });
        }
}

使用 Aspire時,配置將通過環境變量完成,並由 AppHost 設置。 若要啟用遠端工作階段,必須啟用選項:

...

var coreApp = builder.AddProject<Projects.CoreApplication>("core")
    .WithHttpHealthCheck()
    .WaitFor(frameworkApp)
    .WithIncrementalMigrationFallback(frameworkApp, options => options.RemoteSession = RemoteSession.Enabled);

...

通訊協定

唯讀工作階段

唯讀會話在不鎖定的情況下擷取會話狀態。 此程序會使用單一 GET 要求,傳回工作階段狀態並立即關閉。

唯讀會話會從架構應用程式擷取會話狀態

可寫入會話

可讀寫的會話需要額外的步驟:

  • 請從只讀會話中相同的 GET 請求開始
  • 保持初始 GET 請求開啟,直到工作階段完成為止
  • 使用額外的 PUT 請求來更新狀態
  • 只有在更新完成後才關閉初始請求

可寫入的會話狀態通訊協議開頭與唯讀相同