在ASP.NET Core的Authorize-Attribute中将存储库与DbContext一起使用:“无法访问已处置的对象" [英] Use repository with DbContext in ASP.NET Core Authorize-Attribute: "Cannot access a disposed object"

查看:219
本文介绍了在ASP.NET Core的Authorize-Attribute中将存储库与DbContext一起使用:“无法访问已处置的对象"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于第三方身份验证,我需要一个自定义的Authorize属性.在这里,需要一个存储库(SessionManager)类来检查用户是否已登录.

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

在类似sessionManager.GetCurrentSessionAsync()的情况下,发生以下异常:

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

无法访问已处置的对象.导致此错误的常见原因是 放置从依赖项注入中解决的上下文,并 然后稍后尝试在您的其他位置使用相同的上下文实例 应用.如果您在 上下文,或将上下文包装在using语句中.如果你是 使用依赖注入,您应该让依赖注入 容器负责处理上下文实例.对象名称: "AsyncDisposer".

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'.

我知道这一点,因此请勿自行处置. VBSessionManager将我的DbContext注入了其构造函数.在内部GetCurrentSessionAsync cookie中,使用LinQ数据库查询进行了检查.因此,没有调用Disposeusing指令之类的东西.

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.

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 class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public async void OnAuthorization(AuthorizationFilterContext context)
    {
        // …

        await something;

        // …
    }
}

具有方法async void几乎总是不好的想法.异步方法应返回Task,以使调用者能够确定异步过程的结果.

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.

如果需要异步处理,则应该使void方法异步,而应实现

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)

如您所见,此方法返回Task,因此它可以正确地执行异步进程.在您的情况下,您想await方法内部的内容,只需执行以下操作即可:

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;

        // …
    }
}

现在,使用返回Task的适当异步方法,调用系统将能够正确使用该方法,并且继续进行请求处理将等待您的授权过滤器得到处理.

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.

这篇关于在ASP.NET Core的Authorize-Attribute中将存储库与DbContext一起使用:“无法访问已处置的对象"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆