ASP.NET에서 인증 필터를 만들어 쓰는데 DbContext 에러가 났다.
어처구니 없는 실패
ASP.NET에서 권한 레벨 기능을 도입하는 중에 IAuthorizationFilter를 구현한 커스텀 필터를 제작해 사용하려 했는데, 다음과 같은 에러가 지속적으로 발생했다.
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.GetRolesAsync(TUser user, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Identity.UserManager`1.GetRolesAsync(TUser user)
at [FILTERED].RequiresPermissionAttribute.OnAuthorization(AuthorizationFilterContext context) in Q:\Works\naits__\[FILTERED]\[FILTERED]\Filters\RequiresPermissionAttribute.cs:line 43
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_1(Object state)
at System.Threading.QueueUserWorkItemCallback.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
이 예외는 이전 DbContext
의 작업이 완료되기 전에 새 DbContext
작업이 시작될 경우 발생하는 문제인데, 문제는 async/await도 제대로 작성한 마당에 왜 이런 비동기 문제가 발생하는지 한동안 알 수 없었다는 것이다.
IAuthorizationFilter, IAsyncAuthorizationFilter
거의 한 시간을 삽질하다가, 문득 이상한 걸 발견했다. 그건 바로 내 필터의 OnAuthorization
메서드가 async void
로 구현되어 있다는 것.
요새 하도 WPF를 만지작거려서 그런가, 무턱대고 OnAuthorization
메서드의 타입이 void
이니 async void
로 구현해서 UserManager
, RoleManager
등의 비동기 메서드를 await
하고자 했던 것이다.
당연히, 이는 올바른 사용 방법이 아니었다.
비동기 필터는 비동기 필터 전용 인터페이스를 구현하도록 하자
닷넷에는 비동기 필터를 위한 IAsyncAuthorizationFilter
라는 인터페이스가 따로 존재한다. 이 인터페이스의 실제 인증 처리 부분은 Task OnAuthorizationAsync
로, 확실하게 비동기 처리를 위해 디자인되어 있다.
따라서 이 인터페이스를 구현한 필터를 사용하면, 비동기 액션을 처리할 때 정상적으로 필터 처리가 우선 완료된 뒤 액션이 처리되게 된다.
결론
시간이 촉박하더라도 작업 내용을 항상 꼼꼼히 체크하도록 하자. 인텔리센스 자동완성으로 IAuthorizationFilter
를 칠 때 바로 아래에 IAsyncAuthorizationFilter
가 표시됐음에도 불구하고 이런 어처구니없는 실패를 겪었다.
이런 일이 발생할 때마다 자괴감이 든다.
댓글
댓글 쓰기