无法访问已释放的对象 Asp.net Identity Core。

13

我遇到了这个错误

System.ObjectDisposedException
  HResult=0x80131622
  Message=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.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.AsyncDisposer.Dispose()
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.<MoveNext>d__5.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<ExecuteSingletonAsyncQuery>d__21`1.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Identity.UserManager`1.<FindByNameAsync>d__78.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Identity.UserValidator`1.<ValidateUserName>d__6.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Identity.UserValidator`1.<ValidateAsync>d__5.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Identity.UserManager`1.<ValidateUserAsync>d__172.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Identity.UserManager`1.<CreateAsync>d__74.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Identity.UserManager`1.<CreateAsync>d__79.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at EmployeeController.<Create>d__3.MoveNext() in Controllers\EmployeeController.cs:line 31

以下是需要翻译的代码:

public class EmployeeController : Controller
{
    private readonly UserManager<Employee> userManager;
    public EmployeeController(UserManager<Employee> userManager)
    {
        this.userManager = userManager;
    }

    public ActionResult<string> Get()
    {
        return "Test Employee";
    }

    [HttpPost("Create")]
    public async void Create([FromBody]string employee)
    {
        var user = new Employee { UserName = "test@gmail.com", Email = "test@gmail.com" };
        var d = await userManager.CreateAsync(user, "1234567");
    }
}

Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
         options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddIdentity<Employee, IdentityRole>(opts =>
       {
           opts.Password.RequireDigit = false;
           opts.Password.RequireLowercase = false;
           opts.Password.RequireUppercase = false;
           opts.Password.RequireNonAlphanumeric = false;
           opts.Password.RequiredLength = 7;
           opts.User.RequireUniqueEmail = true;

       }).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

        services.AddAuthentication(opts =>
        {
            opts.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(cfg =>
        {
            cfg.RequireHttpsMetadata = false;
            cfg.SaveToken = true;
            cfg.TokenValidationParameters = new TokenValidationParameters()
            {
                // standard configuration
                ValidIssuer = Configuration["Auth:Jwt:Issuer"],
                ValidAudience = Configuration["Auth:Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"])),
                ClockSkew = TimeSpan.Zero,

                // security switches
                RequireExpirationTime = true,
                ValidateIssuer = true,
                ValidateIssuerSigningKey = true,
                ValidateAudience = true
            };
        });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseAuthentication();
        app.UseMvc();
    }
}

我需要在我的DI中注册一些东西吗? 我的理解是AddEntityFrameworkStores<ApplicationDbContext>()就足够了。

2个回答

40

你是一个 async void 的受害者:

[HttpPost("Create")]
public async void Create([FromBody]string employee)
{
    var user = new Employee { UserName = "test@gmail.com", Email = "test@gmail.com" };
    var d = await userManager.CreateAsync(user, "1234567");
}

您正在调度一个异步操作,但是没有等待它完成,而且在CreateAsync中的await context.SaveChangesAsync()之前上下文将被销毁。

快速明显的解决方案:

[HttpPost("Create")]
public async Task Create([FromBody]string employee)
{
    var user = new Employee { UserName = "test@gmail.com", Email = "test@gmail.com" };
    var d = await userManager.CreateAsync(user, "1234567");
}

然而,你应该始终从 Action 返回 IActionResult。 这样可以更轻松地更改响应代码并显示你的意图:

[HttpPost("Create")]
public async Task<IActionResult> Create([FromBody]string employee)
{
    var user = new Employee { UserName = "test@gmail.com", Email = "test@gmail.com" };
    var d = await userManager.CreateAsync(user, "1234567");

    if (d == IdentityResult.Success)
    {
        return Ok();
    }
    else 
    {
        return BadRequest(d);
    }
}

啊,我不知道我的快速模拟测试方法并使用void会在异步中引起这样的问题。所以Task在异步中相当于void? - chobo2
@chobo2 是的,没错。async void 是合法的,但是非常危险。 - Camilo Terevinto
很酷,还有一个问题。我看到你在badrequest中返回对象,其中会有“succeeded:false”,这是要检查的重点还是只需检查400状态代码? - chobo2
@chobo2 那只是一个例子,可能远非最佳。只是为了指出您可以将一些数据与400一起返回给客户端。 - Camilo Terevinto
啊,我明白了,我认为你发送回来的东西还不错,但它包含了所有JSON格式的错误。 - chobo2

0
我曾经遇到过同样的问题,我忘记在我的StartUp.cs文件中添加了两行代码:

services.AddScoped<UserManager, UserManager>();
services.AddScoped<RoleManager, RoleManager>();

添加这两行代码后,程序就可以正常运行了!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接