views:

52

answers:

2

Hi, I have a sign-up page where the user can input his FirstName, LastName, Email and Password, along with other fields.

I have bound validation attributes to this Model (called "User" and created via LINQtoSQL) and all works well.

Model code:

[MetadataType(typeof(UserValidation))]
public partial class User { }

[Bind(Exclude = "UserID")]
[PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "The password and confirm password don't match.")]
public class UserValidation 
{
    [Required(ErrorMessage = "First name required"), StringLength(20, MinimumLength=3, ErrorMessage = "Must be between 3 and 20 characters")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Last name required"), StringLength(20, MinimumLength = 3, ErrorMessage = "Must be between 3 and 20 characters")]
    public string LastName { get; set; }

    [Required(ErrorMessage = "Email address required"), RegularExpression("^[a-z0-9_\\+-]+(\\.[a-z0-9_\\+-]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,4})$", ErrorMessage = "Must be a valid email address")]
    public string Email { get; set; }

    [Required(ErrorMessage = "Password required"), StringLength(20, MinimumLength = 6, ErrorMessage = "Password must be between 6 and 20 characters")]
    public string Password { get; set; }

    [Required(ErrorMessage = "Confirm password required"), StringLength(20, MinimumLength = 6, ErrorMessage = "Password must be between 6 and 20 characters")]
    public string ConfirmPassword { get; set; }

}

After sign-up and login, I want the user to be able to edit their FirstName, LastName and Email (lets call these "Account" fields) in one View and "Password" in another. This is where my problem lies.

When I submit a form updating the Account fields data via the same Model ("User") used in the sign-up, the IsValid method flairs up a ModelState error for the missing "Password" field.

Controller code:

    //
    // GET /Talent/Account
    public ActionResult Account()
    {
        string cookieUser = User.Identity.Name;
        User user = userRepository.GetUserByEmail(cookieUser);
        return View(user);
    }

    // POST /Talent/Account
    [HttpPost]
    public ActionResult Account(User model)
    {

        if (ModelState.IsValid)
        {
    // do something
            ModelState.AddModelError("", "All good.. "+ model.FirstName + " - " + model.LastName);
        }

        return View(model);
    }

How can I get around this?? Best practice etc...

+2  A: 

In Model-View-ViewModel (MVVM) fashion, you should create two ViewModels: one for creating a new account, and the other for editing the name fields without the password required.

Apply the validation attributes to your ViewModels instead of directly to your Models.

Some sample wrapper ViewModel classes:

        [Bind(Exclude = "UserID")]    
        public class UserForAccountEdit
        {
            public User UserAccount { get; set; }

            [Required(ErrorMessage = "First name required"), StringLength(20, MinimumLength=3, ErrorMessage = "Must be between 3 and 20 characters")]  
            public string FirstName  
            {  
              get 
                   { return UserAccount.FirstName };  
              set
                   { UserAccount.FirstName = value; }
            }  

            ...
        }    



        [Bind(Exclude = "UserID")]
        [PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "The password and confirm password don't match.")]
        public class UserForAccountCreation
        {
            public User UserAccount { get; set; }

            [Required(ErrorMessage = "First name required"), StringLength(20, MinimumLength=3, ErrorMessage = "Must be between 3 and 20 characters")]  
            public string FirstName  
            {  
              get 
                   { return UserAccount.FirstName };  
              set
                   { UserAccount.FirstName = value; }
            }  

            ...
        }
Dave Swersky
Thanks for the advice. Any basic code examples on how to implement it?
shahid81
Create two "wrapper" classes for your User L2SQL class. One is for account creation, the other for editing without the password required. Apply the validation attributes to those wrapper classes instead of using the Metadata attribute to apply them directly to the L2SQL generated class.
Dave Swersky
Makes more sense, need to see it in action. Do you have a code example please?
shahid81
@shahid81 - Find any tutorial on object oriented C# subclassing.
jfar
@jfar: Actually LINQ to SQL doesn't support subclassing except for in cases of discriminator columns, which might pollute his model. Updating with an example.
Dave Swersky
@Dave Thanks for the example. A few questions... Do I still use MetaDatType to bind it the partial User class? And how and what do I call from my controller action? UserForAccountCreation or UserForAccountEdit?
shahid81
@shahid81: No you would not use the MetaDataType anymore. That is used to bind attributes to the L2SQL class via the partial class. The ViewModel classes should be the ones to carry the validation attributes, with a "naked" L2SQL User class held internally.
Dave Swersky
@Dave, see answer below please and advise.
shahid81
A: 

I tried to implement you code like below but the 'model.LastUpdated' doesn't get recognised now??

[HttpPost] 
public ActionResult EditAccount(UserForAccountEdit model) 
{ 
    if (ModelState.IsValid) 
    { 
          model.LastUpdated = DateTime.Now;
          userRepository.Save(); 
    } 
    return View(model); 
} 

Is this rightly implemented or am I doing something wrong?

shahid81
Remember that the actual User object you're going to save is now a property of the ViewModel. If "LastUpdated" is a property of User, then: model.UserAccount.LastUpdated
Dave Swersky
Thanks that help. But now when i tried to run the app i get an error when it runs the validation 'Object reference not set to an instance of an object.' on the line 'public string FirstName { get { return UserAccount.FirstName; } set { UserAccount.FirstName = value; } }'
shahid81