1

I am trying to make a form to register new user and save data into Entity Framework using MVC pattern; the thing is that I am trying to compare the entered password as following (code is written in the the model)

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

[DataType(DataType.Password)]
[Compare("tPassword", ErrorMessage = "The password and confirmation password does not match.")]
public string Confirmpassword { get; set; }

The next step is to hash the password and save it (I created a new class called Security with the following method, and other methods to verify the password), following is the code:

public static void HashAndSavePassword(string password, RegisterTable usr)
{
    var v = new Rfc2898DeriveBytes(password, 16, 3987);
    usr.tPassword = Convert.ToBase64String(v.GetBytes(25), Base64FormattingOptions.None);
    usr.Salt = Convert.ToBase64String(v.Salt, Base64FormattingOptions.None);
}

Then in the controller I used following to call the method that is responsible of password hashing and then save user data into Entity Framework:

public ActionResult Register(RegisterTable user)
{
    if (ModelState.IsValid)
    {
         // To check if username already exist
         var searchUserName = db.RegisterTables.Where(x => x.tUserName.Equals(user.tUserName)).FirstOrDefault();

         if (searchUserName == null)
         {
             Security.HashAndSavePassword(user.tPassword, user);

             db.RegisterTables.Add(user);
             db.SaveChanges();
             ModelState.Clear();
             return RedirectToAction("Login");
         }
         else ModelState.AddModelError("", "User is already Registred.");            
     }

     return View(user);
 }

The problem is that I am getting the following error when I run the code:

enter image description here

But the code will work normally if I delete this line:

[Compare("tPassword", ErrorMessage = "The password and confirmation password does not match.")]

Can anyone explain to me why this is happening and how to solve this problem?

1 Answer 1

1

You have ValidateOnSaveEnabled set to true on your DbContext

context.Configuration.ValidateOnSaveEnabled = true;

This means that your validation will be executed twice. First by the model binder (done by MVC) - this will not throw error, because both passwords are same. But then in your HashAndSavePassword you have ConfirmPassword in plain text and tPassword in hash form. So EF will throw error when you call save.

You can:

  1. turn off EF model validation by setting ValidateOnSaveEnabled to false.
  2. in HashAndSavePassword function also hash ConfirmPassword property (this is hack)
  3. use different model in action and then convert to model used in EF. (this is referred)

Your DB model

public class RegisterTable
{
    public int Id { get; set; }
    public string tPassword { get; set; }
    public string tUserName { get; set; }
    public string Salt { get; set; }

    ...
}

Your view model:

public class RegisterModel
{
    // put username validation rules here
    public string UserName { get; set; }

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

    [DataType(DataType.Password)]
    [Compare("Password", ErrorMessage = "The password and confirmation password does not match.")]
    public string Confirmpassword { get; set; }

    ...

    public RegisterTable Map()
    {
        var v = new Rfc2898DeriveBytes(this.Password, 16, 3987);
        return new RegisterTable()
        {

            Salt = Convert.ToBase64String(v.Salt, Base64FormattingOptions.None),
            tPassword = Convert.ToBase64String(v.GetBytes(25), Base64FormattingOptions.None),
            tUserName = this.UserName
        };
    }
}

Your action

public ActionResult Register(RegisterModel user)
{
    if (ModelState.IsValid)
    {
        // To check if username already exist
        var searchUserName = db.RegisterTables.Where(x => x.tUserName.Equals(user.UserName)).FirstOrDefault();

        if (searchUserName == null)
        {
            var dbUser = user.Map();

            db.RegisterTables.Add(user);
            db.SaveChanges();
            ModelState.Clear();
            return RedirectToAction("Login");
        }
        else ModelState.AddModelError("", "User is already Registred.");
    }

    return View(user);
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, its working, but i didnt understand the third point, what do you mean by converting a model to EF model, can you provide some more information or a good tutorials that explains it? thanks again
Right now you use RegisterTable class as parameter in Register action. That is usually not a good idea, because your DB model will have different set of properties than your registration form. For example: there is no need to have ConfirmPassword field on your DB model, because you don't need to store that in DB. And your DB model will have properties, that should never be exposed to views (e.g. IsDeleted, DateModified, ...). So it's better to have model that describes your view form, validate that model and then convert it to different DB or service model.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.