一、前言
我們都知道,ASP.NET Core作為最新的框架,在MVC5和ASP.NET WebForm的基礎上做了大量的重構。如果我們想使用以前版本中的HttpContext.Current的話,目前是不可用的,因為ASP.NET Core中是並沒有這個API的。
當然我們也可以通過在Controller中訪問HttpContext,但是某些情況下,這樣使用起來還是不如HttpContext.Current方便。
二、IHttpContextAccessor
利用ASP.NET Core的依賴注入容器系統,通過請求獲取IHttpContextAccessor接口,我們擁有模擬使用HttpContext.Current這樣API的可能性。但是因為IHttpContextAccessor接口默認不是由依賴注入進行實例管理的。我們先要將它注冊到ServiceCollection中:
public void ConfigureServices(IServiceCollection services) { services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // Other code... }
來模擬一個HttpContext.Current吧:
public static class HttpContext { public static IServiceProvider ServiceProvider; public static Microsoft.AspNetCore.Http.HttpContext Current { get { object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor)); Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext; return context; } } }
其實說到HttpContext.Current就不得不提到多線程問題,在以前的ASP.NET版本中,如果遇到多線程環境很有可能HttpContext.Current為空的情況。說到這個問題以前就是有解決方案的,那就是CallContext;
CallContext 是類似於方法調用的線程本地存儲區的專用集合對象,並提供對每個邏輯執行線程都唯一的數據槽。數據槽不在其他邏輯線程上的調用上下文之間共享。當 CallContext 沿執行代碼路徑往返傳播並且由該路徑中的各個對象檢查時,可將對象添加到其中。
當使用ASP.NET的時候,雖然線城池里的線程是復用的,但是CallContext並不在一個線程的多次使用中共享。因為CallContext是針對邏輯線程的TLS,線程池中被復用的線程是操作系統中的內核對象而不是托管對象。就像數據庫連接池中保存的是非托管資源而不是托管資源。因此,先后執行的兩個托管線程可能在底層復用了一個物理線程(內核對象),但並不能共享同一組CallContext數據槽。就像先后new的兩個SqlConnection對象可能在底層使用了同一個物理連接,但是托管對象的屬性已經被重置。
與此對照的是ThreadStaticAttribute,標記上這個特性的靜態字段是往物理線程的TLS中保存數據(根據MSDN的描述猜的。具體沒試過),因此如果兩個托管線程對象內部使用的是同一個物理線程,則這個字段會復用(在兩個線程通過這一字段訪問同一個數據槽)。
在.NET Core中,也有新的API選擇,AsyncLocal<T>。
三、HttpContextAccessor
我們來看看ASP.NET Core中的IHttpContextAccessor接口實現吧:
public class HttpContextAccessor : IHttpContextAccessor { #if NET451 private static readonly string LogicalDataKey = "__HttpContext_Current__" + AppDomain.CurrentDomain.Id; public HttpContext HttpContext { get { var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle; return handle?.Unwrap() as HttpContext; } set { CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(value)); } } #elif NETSTANDARD1_3 private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>(); public HttpContext HttpContext { get { return _httpContextCurrent.Value; } set { _httpContextCurrent.Value = value; } } #endif }
最后我只能說在ASP.NET Core中是萬物皆DI啊,其實Core中的實現早就為我們想好了這些功能,只是改變了使用方式。
GitHub:https://github.com/maxzhang1985/YOYOFx 如果覺還可以請Star下, 歡迎一起交流。
.NET Core 開源學習群: 214741894
