Usermanager.DbContext已经部署在Middleware中

时间:2016-09-07 11:45:53

标签: asp.net entity-framework asp.net-core

我在Asp.Net Core应用程序中有以下中间件,我需要在其中注入UserManager。当我尝试查询用户FindByIdAsync时,出现以下异常:

ObjectDisposedException: 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 is 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: 'ApplicationDbContext'.

我的中间件:

namespace MyApp
{
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.Extensions.Options;

    public class MyMiddleware<TUser, TRole> 
        where TUser : class where TRole : class 
    {
        private readonly RequestDelegate _next;
        private readonly UserManager<TUser> _userManager;
        private readonly RoleManager<TRole> _roleManager;
        private readonly IOptions<IdentityOptions> _optionsAccessor;

        public UserIdentityMiddleware(RequestDelegate next, UserManager<TUser> userManager, 
            RoleManager<TRole> roleManager, IOptions<IdentityOptions> optionsAccessor)
        {
            _next = next;
            _userManager = userManager;
            _roleManager = roleManager;
            _optionsAccessor = optionsAccessor;
        }

        public async Task Invoke(HttpContext context)
        {
            var user = await _userManager.FindByIdAsync("1");

            var claimsPrincipal = await new UserClaimsPrincipalFactory<TUser, TRole>(
                _userManager, _roleManager, _optionsAccessor).CreateAsync(user);

            context.User.AddIdentities(claimsPrincipal.Identities);

            await _next(context);
        }
    }
}

1 个答案:

答案 0 :(得分:4)

问题的发生是因为中间件本身是一个Singleton,所以它的内部依赖关系也需要是singleton,但是像UserManager这样的标识类默认是每个请求的范围,它们是如何在Startup.ConfigureServices中注册的,也是UserManager的依赖关系(如dbContext)也是每个请求的范围。

通常每个请求的范围是你想要的那些对象,特别是如果它们也用在其他地方,如控制器。

一个解决方案是让您的中间件对UserManager采用构造函数依赖,而不是只在需要时从Invoke方法内部访问,语法将是这样的:

var userManager = context.RequestServices.GetService(UserManager<TUser>);

可能不是完全正确的语法,您可能必须指定实际的用户类型

缺点是这将是servicelocator模式,但它可以解决问题,因为那样你会得到一个UserManager的每个请求,这是它应该如何作用范围

实际上我认为你可以避免使用服务定位器模式,因为Invoke方法是可注入的,你可以像这样在方法签名中添加其他依赖项:

public async Task Invoke(HttpContext context, UserManager<TUser> userManager)
{
    ...
}

但可能还需要提供实际类型而不是TUser