EmailConfirmation 导致错误

EmailConfirmation causes error

我对线程和 C# 还很陌生。我正在尝试实施 Identity,我 运行 遇到了一个问题:当我注册用户并发送确认 link 时,单击 link 导致异常:

我尽量简化我的代码来解释问题。

所以我的 MVC 项目控制器中有 AccountController

namespace SolwayOrder.Controllers {
    public class AccountController: Controller {
       private readonly ApplicationUserManager _manager;
       private readonly SignInManager < IdentityUser, string > _signIn;
       private readonly RoleManager < IdentityRole, int > _roleManager;

       public AccountController(
        ApplicationUserManager manager,
        SignInManager < IdentityUser, string > signIn,
        RoleManager < IdentityRole, int > roleManager
       ) {
        _manager = manager;
        _signIn = signIn;
        _roleManager = roleManager;
       }

       [HttpPost]
       [ValidateAntiForgeryToken]
       public async Task < ActionResult > Register(RegisterModel model, string returnUrl) {
        if (ModelState.IsValid) {
         var user = new IdentityUser(model.Email);
         var result = await _manager.CreateAsync(user, model.Password);
         if (result.Succeeded) {
          var code = await _manager.GenerateEmailConfirmationTokenAsync(model.Email);              var callbackUrl = Url.Action(
           "ConfirmEmail", "Account",
           new {
            userId = model.Email, code = code
           },
           protocol: Request.Url.Scheme);

          await _manager.SendEmailAsync(model.Email,
           "Confirm your account",
           "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
          return Content("Please confirm your account. Link was sent to " + model.Email);
         }
        }
        return View();
       }


       [HttpGet]
       public async Task < ActionResult > ConfirmEmail(string userId, string code) {
        if (userId == null || code == null) return View("Register"); //error
        var result = await _manager.ConfirmEmailAsync(userId, code);
        if (result.Succeeded) {
         return View();
        }
        return View("Register");
       }
    }
}

当我点击我在电子邮件中收到的 link 时出现问题:

A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

Source Error: 


Line 29: 
Line 30:         public async Task<User> FindByUserNameAsync(string username) {
Line 31:             return await Set.FirstOrDefaultAsync(x => x.Email == username);
Line 32:         }
Line 33: 

Source File: C:\Users\Solway\documents\visual studio 2017\Projects\SolwayOrder\SolwayOrder.Repo\Repositories\UserRepository.cs    Line: 31 

我把它修改成到处都是异步方法,在返回Task的地方,我有async和await。

抱歉无法进一步简化问题,我认为这是常见的模式,应该可以理解。如果不是这里有一个 link 到完整的项目: DropBox Link To project

full stack:


[NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.]
   System.Data.Entity.Internal.ThrowingMonitor.EnsureNotEntered() +72
   System.Data.Entity.Core.Objects.ObjectQuery`1.System.Data.Entity.Infrastructure.IDbAsyncEnumerable<T>.GetAsyncEnumerator() +22
   System.Data.Entity.Infrastructure.<FirstOrDefaultAsync>d__25`1.MoveNext() +114
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +28
   SolwayOrder.Repo.<FindByUserNameAsync>d__5.MoveNext() in C:\Users\Solway\documents\visual studio 2017\Projects\SolwayOrder\SolwayOrder.Repo\Repositories\UserRepository.cs:31
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +28
   SolwayOrder.Service.<FindUserByNameAsync>d__7.MoveNext() in C:\Users\Solway\documents\visual studio 2017\Projects\SolwayOrder\SolwayOrder.Service\Security.cs:40
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +28
   SolwayOrder.Identity.<FindByNameAsync>d__5.MoveNext() in C:\Users\Solway\documents\visual studio 2017\Projects\SolwayOrder\SolwayOrder\Identity\UserStore.cs:46
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   Microsoft.AspNet.Identity.CultureAwaiter`1.GetResult() +59
   Microsoft.AspNet.Identity.<ValidateUserName>d__4.MoveNext() +589
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   Microsoft.AspNet.Identity.<ValidateAsync>d__0.MoveNext() +293
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   Microsoft.AspNet.Identity.<UpdateAsync>d__5.MoveNext() +295
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   Microsoft.AspNet.Identity.CultureAwaiter`1.GetResult() +59
   Microsoft.AspNet.Identity.<ConfirmEmailAsync>d__c5.MoveNext() +1100
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +28
   SolwayOrder.Controllers.<ConfirmEmail>d__8.MoveNext() in C:\Users\Solway\documents\visual studio 2017\Projects\SolwayOrder\SolwayOrder\Controllers\AccountController.cs:88
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +58
   System.Web.Mvc.Async.TaskAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult) +97
   System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeAsynchronousActionMethod>b__36(IAsyncResult asyncResult) +17
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +32
   System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +50
   System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +228
   System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +34
   System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +26
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +100
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +13
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36
   System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +12
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +22
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
   System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9987157
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

真正让我吃惊的是,如果我在EmailConfirm Controller上设置断点,一步步走,一切顺利完美,无一例外。

在您的存储库 类(SolwayOrder.Repo.RepositorySolwayOrder.Repo.UserRepositorySolwayOrder.Repo.UnitOfWork)中,您将 AppDbContext 的 Singleton 实例保留在私有字段中并在所有操作。

如果多个并发请求正在执行,您可以使用明确的消息 'A second operation started on this context before a previous asynchronous operation completed' 捕获该异常。

作为规则,您应该在每个操作中创建一个上下文实例,并在使用后处理它。不要害怕由于每次上下文创建都打开连接而导致性能下降。 Entity Framework 建立在使用 connection pooling 的 ADO.NET 之上。与单例上下文相比,性能不应降低。

例如你的SaveChangesAsync方法

public Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
    return _context.SaveChangesAsync(cancellationToken);
}

应该改写为:

public async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
    using (var ctx = new AppDbContext())
    {
        return await ctx.SaveChangesAsync(cancellationToken);
    }
}

请注意,您不能像以前那样将方法保持为非异步。就方法中的上下文而言,您应该等待 SaveChangesAsync 操作完成。

adding await didn't help.

明确一点,除了“添加await”,您还需要

  • 创建函数async
  • return IdentityUser

标准代码为

    public async Task<IdentityUser> FindByNameAsync(string userName) {
        var user = await _security.FindUserByNameAsync(userName);

        return getIdentityUser(user);

    }

请注意,您应该一直使用“async”,因此您的FindUserByNameAsync 也应该阅读

    public async Task<User> FindUserByNameAsync(string userName) {
        return await _unitOfWork.UserRepository.FindByUserNameAsync(userName);
    }