EmailConfirmation causes error

2019-08-23 05:28发布

I am fairly new to threading and C#. I'm trying to implement Identity, I run into a problem: when I register a user and send the confirmation link, clicking on the link causes an exception:

I try to do my best to simplify my code to explain problem.

so I have AccountController in my Controllers of MVC project:

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

The problem arise when I click the link which I got on Email:

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 

I modified it so everywhere for asynchronous methods, where Task is returned, I have async and await.

sorry couldn't simplify the question more, I think this is common pattern and should be understandable. if not here's a link to full project: 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

what really surprises me is that if I set breakpoint on EmailConfirm Controller, and go step by step, everything goes smooth and perfect, no exception.

2条回答
乱世女痞
2楼-- · 2019-08-23 05:55

In your repository classes (SolwayOrder.Repo.Repository, SolwayOrder.Repo.UserRepository, SolwayOrder.Repo.UnitOfWork) you keep Singletone instance of AppDbContext in private field and use it in all operations.

If several concurrent requests are executing, you catch that exception with clear message 'A second operation started on this context before a previous asynchronous operation completed'.

As a rule you should create instance of a context in each operation and dispose it after the use. Do not be afraid of performance degradation due to connection opening for every context creation. Entity Framework is built over ADO.NET that uses connection pooling. Performance should not degrade comparing to Singleton context.

For example your SaveChangesAsync method

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

should be rewritten as:

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

Note, that you can't keep the method non-async as previously. As far as your context is disposed in the method, you should await for SaveChangesAsync operation to complete.

查看更多
太酷不给撩
3楼-- · 2019-08-23 06:08

adding await didn't help.

Just to be clear, aside from "adding await", you also need to

  • make the function async
  • return the IdentityUser

The standard code would be

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

        return getIdentityUser(user);

    }

Notice that you should use "async all the way", so also your FindUserByNameAsync sould read

    public async Task<User> FindUserByNameAsync(string userName) {
        return await _unitOfWork.UserRepository.FindByUserNameAsync(userName);
    }
查看更多
登录 后发表回答