![](/img/trans.png)
[英]Cannot access a disposed object in ASP.NET Core when injecting DbContext
[英]Use repository with DbContext in ASP.NET Core Authorize-Attribute: “Cannot access a disposed object”
對於第三方身份驗證,我需要一個自定義的Authorize
屬性。 在這里,需要一個存儲庫( SessionManager
)類來檢查用戶是否已登錄。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter {
public async void OnAuthorization(AuthorizationFilterContext context) {
var sessionManager = (VBSessionManager)context.HttpContext.RequestServices.GetService(typeof(VBSessionManager));
var user = await sessionManager.GetCurrentSessionAsync();
if (user == null) {
context.Result = new UnauthorizedResult();
return;
}
}
}
在類似sessionManager.GetCurrentSessionAsync()
,會發生以下異常:
無法訪問已處置的對象。 導致此錯誤的常見原因是,處理從依賴項注入中解決的上下文,然后稍后嘗試在應用程序中的其他位置使用相同的上下文實例。 如果在上下文上調用Dispose()或將上下文包裝在using語句中,則可能會發生這種情況。 如果使用依賴項注入,則應讓依賴項注入容器負責處理上下文實例。 對象名稱:“ AsyncDisposer”。
我知道這一點,請勿自行處置。 VBSessionManager
在其構造函數中注入了我的DbContext
。 在內部,使用LinQ數據庫查詢檢查了GetCurrentSessionAsync
Cookie。 因此, Dispose
using
指令或類似using
調用Dispose
。
VBSessionManager
注入 public class VBSessionManager {
readonly VBDbContext db;
readonly IHttpContextAccessor contextAccessor;
const string sessionHashCookieName = "xxx";
VBSession currentSession;
public VBSessionManager(VBDbContext db, IHttpContextAccessor contextAccessor) {
this.db = db;
this.contextAccessor = contextAccessor;
}
public async Task<VBSession> GetCurrentSessionAsync() {
if (currentSession == null) {
string sessionCookie = GetCookieWithoutPrefix(sessionHashCookieName);
currentSession = await GetSessionAsync(sessionCookie);
if (currentSession == null) {
var cookieUser = GetUserFromCookiePassword().Result;
// No session detected
if (cookieUser == null) {
return null;
}
currentSession = db.Sessions.FirstOrDefault(s => s.UserId == cookieUser.Id);
}
}
return currentSession;
}
// ...
}
services.AddDbContext<VBDbContext>(options => {
string connectionString = Configuration.GetValue<string>("VBConnectionString");
options.UseMySql(connectionString,
mySqlOptions => {
mySqlOptions.ServerVersion(new Version(10, 2, 19), ServerType.MariaDb);
}
);
bool isDev = CurrentEnvironment.IsDevelopment();
options.EnableSensitiveDataLogging(isDev);
});
services.AddScoped<VBSessionManager>();
public async void OnAuthorization(AuthorizationFilterContext context) {
在這里重要的是使用async void
, 根據David Fowler的說法 ,這總是很糟糕的。 使用此處的設置,無法await
對OnAuthorization
本身的調用,這意味着正在發生類似以下的事情:
OnAuthorization
方法之前,需要一段時間創建VBSessionManager
和VBDbContext
作用域實例。 OnAuthorization
執行並調用VBSessionManager.GetCurrentSessionAsync
,在所述方法有機會完成之前返回(由於使用了async
/ await
)。 OnAuthorization
后,將部署VBDbContext
IDisposable
VBDbContext。 VBSessionManager.GetCurrentSessionAsync
內部的代碼仍在運行-它嘗試使用已處置的VBDbContext
實例。 在您的情況下使用async void
的原因僅僅是因為這是在IAuthorizationFilter
接口中聲明的-您要使用await
,而這樣做的唯一方法是將實現方法標記為async
(您不能使其成為async Task
因為那將無法實現該接口)。
關於此問題的解決方案,我同意加布里埃爾·盧西(Gabriel Luci)的觀點,那就是使用基於策略的授權 。
public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public async void OnAuthorization(AuthorizationFilterContext context)
{
// …
await something;
// …
}
}
使方法async void
幾乎總是一個壞主意 。 異步方法應返回Task
以使調用者能夠確定異步過程的結果。
由於要實現IAuthorizationFilter
,因此要實現同步授權過濾器。 當您不需要異步執行某些操作時,可以使用它。 例如,如果您只需要查看一些參數,然后做出一些決定來確定是否允許訪問,則為true。
如果您需要異步進程,你不應該做的void
方法異步而是實現IAsyncAuthorizationFilter
。 這是用於實現異步授權過濾器的接口。 在這種情況下,您需要實現的方法看起來有些不同:
Task OnAuthorizationAsync(AuthorizationFilterContext context)
如您所見,此方法返回一個Task
以便它可以正確執行異步進程。 在您的情況下,您想await
方法中的某些內容,則可以這樣做:
public class VBAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
// …
await something;
// …
}
}
現在,有了返回Task
的適當異步方法,調用系統將能夠正確使用該方法,並且繼續進行請求處理將等待您的授權過濾器得到處理。
似乎使用async
會導致問題。 當我將OnAuthorization
更改為這樣的同步方法時,我沒有收到任何錯誤:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter {
public void OnAuthorization(AuthorizationFilterContext context) {
var sessionManager = (VBSessionManager)context.HttpContext.RequestServices.GetService(typeof(VBSessionManager));
var user = sessionManager.GetCurrentSessionAsync().Result;
if (user == null) {
context.Result = new UnauthorizedResult();
return;
}
}
}
不知道這些屬性(或僅是AuthorizeAttribute
)是否不是設計為異步工作的。 對我來說,當前的解決方法是使用syn方法。 我也認為這不會降低性能。 但是,如果有人知道背景,甚至知道如何使用異步屬性,那么我會對另一個answear感到滿意。
OnAuthorization
將OnAuthorization
方法用於驗證授權。 這只是一個通知,“嘿,授權正在進行中”。
就是說, 有人為此使用了它 。 但是,由於您將其聲明為async void
,因此沒有任何方法等待此方法完成。 那就是您例外的根源:在進行數據庫調用時,請求已經完成,並且上下文已處置。 您可以只刪除async
...。
但是正確的解決方案是使用IAuthorizationHandler
,其名稱專門用於處理授權。 它有一個HandleAsync
方法,這是一個實際上正在等待的正確的async
方法(它等待您對授權的決定,然后繼續)。
看看Microsoft員工提供的答案 。 設置處理程序,然后將其與常規的AuthorizeAttribute
一起使用,如下所示:
[Authorize(Policy = "MyCustomPolicy")]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.