會話狀態是許多 Web 應用程式的關鍵元件,跨 HTTP 請求儲存使用者特定的資料。 從 ASP.NET Framework 遷移到 ASP.NET Core 時,會話狀態會帶來獨特的挑戰,因為這兩個框架處理會話的方式非常不同。
為什麼工作階段移轉很複雜
ASP.NET Framework 和 ASP.NET Core 對會話管理有著根本不同的方法:
- ASP.NET Framework 提供自動物件序列化和內建會話鎖定
- ASP.NET Core 需要手動序列化,並且不提供會話鎖定保證
這些差異表示您無法簡單地將工作階段程式碼從 Framework 移至 Core 而不進行變更。
移轉策略概觀
在移轉期間,您有三種主要方法來處理會話狀態:
- 完全重寫 - 重寫所有會話程式碼以使用 ASP.NET Core 的原生會話實作
- 使用個別會話的增量 - 逐步移轉組件,讓每個應用程式維護自己的會話狀態
- 增量式且共用工作階段 - 在框架和核心應用程式之間共享工作階段資料時遷移元件
對於大部分的應用程式,移轉至 ASP.NET 核心工作階段 可提供最佳效能和可維護性。 不過,較大的應用程式或具有複雜會話需求的應用程式可能會受益於使用 System.Web 配接器的累加式方法。
選擇移轉方法
您有三個主要選項可將工作階段狀態從 ASP.NET Framework 移轉至 ASP.NET Core。 您的選擇取決於您的移轉時間表、您是否需要同時執行兩個應用程式,以及您願意重寫多少程式碼。
快速決策指南
回答以下問題以選擇您的方法:
您是否正在進行完全重寫或增量遷移?
- 完成重寫→ 內建 ASP.NET Core 會話
- 增量移轉 → 繼續問題 2
您的 ASP.NET Framework 和 ASP.NET Core 應用程式是否需要存取相同的工作階段資料?
- 是,遠端應用程式會話需要共用會話→
- 否,個別會話可以→ Wrapped 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 核心配置:
呼叫 AddRemoteAppSession 和 AddJsonSessionSerializer 註冊已知的工作階段項目類型:
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請求來更新狀態 - 只有在更新完成後才關閉初始請求