Use repository with DbContext in ASP.NET Core Authorize-Attribute: "Cannot access a disposed object"

asp.net-core attributes c# dbcontext entity-framework-core

Question

For third party authentication, I need a custom Authorize attribute. Here a repository (SessionManager) class is required to check if the user is logged in.

[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;
        }
    }
}

In the like sessionManager.GetCurrentSessionAsync() the following exception occur:

Cannot access a disposed object. A common cause of this error is disposing a context 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, or wrapping the context 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: 'AsyncDisposer'.

I'm aware of this and don't to any disposing on my own. VBSessionManager got my DbContext injected in its constructor. Inside GetCurrentSessionAsync cookies were checked with LinQ database queries. So no calling of Dispose, using directives or something like that.

Injection in 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;
    }
    // ...
}

Injection of services

        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>();
1
0
12/19/2018 4:25:32 PM

Popular Answer

public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public async void OnAuthorization(AuthorizationFilterContext context)
    {
        // …

        await something;

        // …
    }
}

Having a method async void is almost always a bad idea. Asynchronous methods should return a Task to make callers able to determine the result of the asynchronous process.

Since you are implementing IAuthorizationFilter, you are implementing a synchronous authorization filter. You use this when you do not need to do something asynchronously. This is for example true if you just need to look at some of the parameters and then have some ruling to determine whether access is allowed or not.

If you require asynchronous processes, you should not make the void method asynchronous but instead implement IAsyncAuthorizationFilter. This is the interface for implementing an asynchronous authorization filter. In that case, the method you need to implement looks a bit different:

Task OnAuthorizationAsync(AuthorizationFilterContext context)

As you can see, this method returns a Task so it can properly do asynchronous processes. In your case, where you want to await something inside of the method, you can just do it:

public class VBAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
{
    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        // …

        await something;

        // …
    }
}

Now, with a proper asynchronous method that returns a Task, the calling system will be able to consume the method properly and the continuation of the request handling will wait for your authorization filter to be processed.

1
12/19/2018 5:10:43 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow