---
Title: 使用Middleware做權限驗證調用EFCore DBContext物件存在週期問題
tags: ASP.NET Core
---
# [Asp.net Core]使用Middleware做權限驗證調用EFCore DBContext物件存在週期問題
程式碼:
https://github.com/s123600g/AuthorizeErrorEFCoreSample
此筆記為紀錄個人開發驗證權限遇到問題,在自訂Token驗證權限過程中,因不小心把**DBContext**給釋放掉,導致後面頁面`Controller-Action`內DBContext物件實際上是變成空物件參考狀態,在調用查詢時會噴出以下錯誤
```
ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances. Object name: 'NorthwindContext'.
```
### `Sartup.cs`
在Pipline管道上,路由進入到對應頁面`Controller-Action`內之前,會先跑第一道中繼站
```csharp=
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 401)
{
context.Response.Redirect(Configuration.GetValue<string>("VerifyFailedRedirectUrl"));
}
});
```
在這中繼站第一行`await next();`會先暫停本身動作,讓後面的中繼站跑完才會再回來接續,當回來後會再判斷後面的權限驗證過程中是否未授權,如果是未授權`context.Response.StatusCode`會是401狀態碼會觸發轉址到指定頁面去。
後面的中繼站流程順序
* 1. `app.UseAuthentication();`
* 2. `app.UseAuthorization();`
* 3. `app.UseEndpoints` -->當跑到這裡的時候,因Action上方有宣告一個來自於**Microsoft.AspNetCore.Authorization** -->`[Authorize]`
會在觸發到`Services`/`AuthTokenHandler.cs`內`HandleAuthenticateAsync()`進行驗證,原因在於內部調用DbContext處理過程
```csharp=
using (db)
{
int GetDbRecord = db.Employees
.Where(data => data.EmployeeId == int.Parse(CurrentUIDSessionToken))
.Count();
CheckResult = GetDbRecord != 0 ? true : false;
}
```
因為DBcontext在`Startup.cs`透過`AddDbContext`注入是屬於Scope範圍型態服務,也就是當有請求進入到這請求處理結束生命週期內,會建立一個DbContext物件,並且會一直使用這同一個物件。
> 有關於Scope範圍型態
> https://docs.microsoft.com/zh-tw/dotnet/core/extensions/dependency-injection#scoped
在這`using(){}`處理區塊結束時,會將此DbContext物件給釋放掉,導致驗證權限如果通過後,進入到`EmployeesController`內`Index()`使用DbContext物件進行查詢時,因為物件本身已經被釋放掉,所以會發生ObjectDisposedException錯誤。

正確版本要改為
```csharp=
int GetDbRecord = db.Employees
.Where(data => data.EmployeeId == int.Parse(CurrentUIDSessionToken))
.Count();
```
讓這個DbContext物件在驗證權限中繼站結束後,進入Action內不會是空物件情況,在整體請求生命週期結束後,整個Scope型態服務物件才會被釋放掉。
```csharp=
[HttpGet]
[Authorize] <-- 進入此Action之前會先跑權限驗證
public async Task<IActionResult> Index()
{
return View(await db.Employees.ToListAsync());
}
```