0

I am trying to implement changing email functionality in my ASP.NET MVC project. The nature of my application requires that email address be unique with each user. So in my ASP.NET Identity implementation I created custom SetEmailAsync() method to throw an ArgumentException when the email address is already in use. The implementation is shown below:

class IdentityUserStore
{
    // Unrelated methods omitted for simplicity
    public Task SetEmailAsync(ApplicationUser User, string email)
    {
        var user = UnitOfWork.Users.FindByEmail(email);
        CheckExistingUser(user);
        user.Email = email;
        return Task.FromResult(0);
    }

    private void CheckExistingUser(User user){
        if (user != null)
        {
            throw new ArgumentException("The Email Address is already in use.");
        }    
    }
}

class AccountController : Controller
{
    // Unrelated Methods omitted for simplicity
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Email(ChangeEmailFormModel model)
    {
        ViewBag.ReturnUrl = Url.Action("Email");
        if (ModelState.IsValid)
        {
            try
            {
                var result = await userManager.SetEmailAsync(User.Identity.GetUserId(), model.NewEmail);
                if (result.Succeeded)
                {
                    return RedirectToAction("Email", new { Message = ManageMessageId.ChangeEmailSuccess });
                }
                else
                {
                    result.Errors.Each(error => ModelState.AddModelError("", error));
                }
            }
            catch(ArgumentException ae)
            {
                ModelState.AddModelError("", ae.Message);
            }
        }
        return View();
    }
}

As you can see, The IdentityUserStore is a custom implementation of UserStore for ASP.NET Identity, which includes the functionality to change/set email address. This class throws an ArgumentException if email address is already being used by an existing User entity. And this exception is supposed to be caught in the AccountController class' method Email(), but it doesnt get caught. Instead, it throws this following error message:

An exception of type 'System.ArgumentException' occurred in MVCApp.Application.dll but was not handled in user code
Additional information: The Email Address is already in use.

So I am totally confused, I thought if an exception is thrown, the client code should be able to catch and handle it. But this isnt happening, the exception thrown by the UserStore is not caught by controller method. Why is this happening? Does it have something to do with the 'await' statement? Can anyone help?

Lord Yggdrasill
  • 3,197
  • 4
  • 26
  • 42
  • Check [this](http://stackoverflow.com/questions/5383310/catch-an-exception-thrown-by-an-async-method) one. Maybe the Rob Church's answer may help you – Qsprec Oct 14 '16 at 05:24
  • @Qsprec: Nope, I dont think Rob Church's answer helps. I changed the signature of CheckExistingUser() to return Task, and the thrown exception is still not caught. All the calling stack return tasks too, I dont think this is where the problem is... – Lord Yggdrasill Oct 14 '16 at 05:30
  • 1
    Is the exception wrapped in an aggregate exception? If you change catch(ArgumentException ae) to catch(Exception e) does it make a difference? – Gavin Oct 14 '16 at 09:47
  • Well I changed to catch the base exception System.Exception, and no difference really. – Lord Yggdrasill Oct 15 '16 at 03:54

1 Answers1

0

Identity framework provides you an option to enforce email uniqueness. This is done in UserValidator<> class which is a part of UserManager:

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    //.. other code
    this.UserValidator = new UserValidator<ApplicationUser>(this)
    {
        RequireUniqueEmail = true,
    };
}

This will prevent duplicate emails to be set. And no need in building it yourself.

trailmax
  • 34,305
  • 22
  • 140
  • 234