5

I am trying to check if Email exists at registration so that there are no duplicate emails in the database. I am doing this with the MVC4 default Internet Application but i have added an Email field to the RegisterviewModel. I also noticed that, this is well done with UserName field but i have no idea how they did it. I tried to follow This blog below but no luck as there is no response when i click create. Tug Berk. When i use firebug i am getting Status: 302 Found when Json action excutes

This is what i have have:

UserProfile

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
}

RegisterViewModel

public class RegisterModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    [Display(Name = "Email")]
    [Remote("DoesUserEmailExist", "Account", HttpMethod = "POST", ErrorMessage = "Email address already exists. Please enter a different Email address.")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.",      MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

Register Action

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Register(RegisterModel model)
    {
        if (ModelState.IsValid)
        {
            try //CreateUserAndAccount
            {
                WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { model.Email });
                WebSecurity.Login(model.UserName, model.Password);
                return RedirectToAction("Index", "Home");

            }
            catch (MembershipCreateUserException e)
            {
                ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

Json Action

[HttpPost]
    public JsonResult DoesUserEmailExist(string email)
    {

        var user = Membership.GetUserNameByEmail(email);

        return Json(user == null);
    }

Edit: Added Register View

     @model Soccer.Models.RegisterModel

     @{
     ViewBag.Title = "Register";
     }

     <hgroup class="title">
     <h1>@ViewBag.Title.</h1>
     <h2>Create a new account.</h2>
     </hgroup>

 @using (Html.BeginForm()) {
 @Html.AntiForgeryToken()
 @Html.ValidationSummary()

 <fieldset>
    <legend>Registration Form</legend>
    <ol>
        <li>
            @Html.LabelFor(m => m.UserName)
            @Html.TextBoxFor(m => m.UserName)
        </li>
        <li>
            @Html.LabelFor(m => m.Email)
            @Html.TextBoxFor(m => m.Email)
        </li>
        <li>
            @Html.LabelFor(m => m.Password)
            @Html.PasswordFor(m => m.Password)
        </li>
        <li>
            @Html.LabelFor(m => m.ConfirmPassword)
            @Html.PasswordFor(m => m.ConfirmPassword)
        </li>
    </ol>
    <input type="submit" value="Register" />
</fieldset>
 }

 @section Scripts {
 @Scripts.Render("~/bundles/jqueryval")
 }

Is there something i shouldn't be doing or is there another simpler option i could take here?

abatishchev
  • 92,232
  • 78
  • 284
  • 421
Komengem
  • 3,496
  • 6
  • 31
  • 56

2 Answers2

8

For starter, i would like to say thanks to JOBG for intracting with me through this. What i showed in the question above should still work if you use Database First as it demands that you set Email column UNIQUE in the database, however to make it work with EF Code First i had to do the following to the register action, also note that with this solution, you don't need Remote Annotation in the model:

Register Action

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Register(RegisterModel model)
    {
        if (ModelState.IsValid)
        {
            // Insert a new user into the database
            using (UsersContext db = new UsersContext())
            {
                UserProfile email = db.UserProfiles.FirstOrDefault(u => u.Email.ToLower() == model.Email.ToLower());
                try
                {
                    // Check if email already exists
                    if (email == null)
                    {
                        WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { Email = model.Email });
                        WebSecurity.Login(model.UserName, model.Password);
                        return RedirectToAction("Index", "Home");
                    }
                    else
                    {
                        ModelState.AddModelError("Email", "Email address already exists. Please enter a different email address.");
                    }
                }
                catch (MembershipCreateUserException e)
                {

                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

Note: Take note that this action validates Email before UserName and that it requires that you take a trip to the database atleast once before you create your object.

Community
  • 1
  • 1
Komengem
  • 3,496
  • 6
  • 31
  • 56
3

Hard to tell without the View code but my first suspect is js, make sure you have jQuery validation plug-in set in place, it should be firing the validation with .blur() event, inspect the page with firebug(or similar) and look for an ajax POST that hits DoesUserEmailExist when the field loses focus, if this is not happening then something is missing in client side as Remote attribute and the service DoesUserEmailExist looks ok.

Side Note: Make sure you have the UNIQUE constraint in the database for the email field, this is the only real way to make sure you have no duplicate emails, even with the DoesUserEmailExist verification, because of concurrency.

JOBG
  • 4,364
  • 4
  • 23
  • 47
  • I have added a Register view. As for UNIQUE constraint, i am not sure if that's possible with code first entity framework. Considering that, this has already been assigned to UserId – Komengem Jan 17 '13 at 05:14
  • You are right EF5 does not support UNIQUE with code first argh, maybe that's one of the reasons why i don't use it, i prefer db first... as for the question, this bundle `~/bundles/jqueryval` renders the needed js so that's not the problem, did you try firebug to check for the request? – JOBG Jan 17 '13 at 05:31
  • I just run it, `Status: 302 Found` That's what i got, something is definitely wrong here – Komengem Jan 17 '13 at 05:44
  • Just a quick notice, i just realised that under Post only the first letter of the email string is assigned to Email. Should it be like that? `Email=k` – Komengem Jan 17 '13 at 06:19
  • 302 Found, there is something wrong with your route, is the action and controller name correct in Remote Attribute? if you put a breakpoint in your `DoesUserEmailExist` Does it gets hit? – JOBG Jan 17 '13 at 06:24
  • Well, everything is noted above in the question. I think its all there – Komengem Jan 17 '13 at 06:40
  • Mmm looking again 302 is a redirect. Is `DoesUserEmailExist` behind some authentication? Maybe you are missing `[AllowAnonymous]`... – JOBG Jan 17 '13 at 20:19
  • Well, i looked into `[AllowAnonymous]` no luck. Is there a difference between `WebSecurity.cs` and `Membership.cs` because if you look at register action, we are calling `WebSecurity.cs` while in Json action `Membership.cs` is called. After all the tries, i feel as though the problem is bewteen those 2 classes. – Komengem Jan 17 '13 at 22:27
  • I just realised something, this method demands that the column in the table is set to UNIQUE.....i may need to find a different method to achieve what i want. – Komengem Jan 17 '13 at 22:50
  • +1 for sticking around, thanks alot. What i did works with `Database First`. The solution i just posted works with `EF Code First`, atleast to my knowledge – Komengem Jan 17 '13 at 23:54