Manuel Rauber
Manuel Rauber

Reputation: 1392

DataValidation Model / ViewModel / Entity Framework Code First

I'm designing a new website with ASP.NET MVC 4 (Beta), VS 11 (Beta), EF 5 (Beta), but this question suits for released versions of ASP.NET MVC 3, VS 2010, EF 4, too.

First step: I'm using Entity Framework Code First approach, for example, I've got following user model:

public class User
{
  [Key]
  public int UserId {get;set;}

  public String LoginName { get; set; }

  public String Password { get; set; }
}

Now, for registration I need another model, the registration model:

public class Registration
{
  public String LoginName { get; set; }

  public String Password { get; set; }

  public String PasswordConfirm { get; set; }
}

This is where my problems begin: Where should I put my DataValidation Annotations? For example the password should be at minimum 10 characters long and the PasswordConfirmed must match Password and so on. Do I have to write this on every model which could do something with the password (I'm thinking of having a ChangePassword model, too)

Another thing is how to deal with the controller. When I display my Registration ViewModel and everything is fine, do I create a User model and assign the variables from Registration ViewModel to it?

Sometimes I've a lot of properties which go to database, but not shown to the user (foreign keys, calculated values etc.).

As thinkink on DRY, I don't want to repeat my self.

What is the best practice for this one?

To be clear: Annotations isn't a need. If there a better ways to validate, I will be glad, if you show them.

Upvotes: 4

Views: 1863

Answers (2)

Patryk Ćwiek
Patryk Ćwiek

Reputation: 14328

I can't say objectively which is 'the best practice', but here's how I see it. If you're binding to the view model, verify the view model, so:

public class Registration
{

    public String LoginName { get; set; }

    [Required]
    [StringLength(50, MinimumLength=10)]
    public String Password { get; set; }

    [Required]
    [StringLength(50, MinimumLength=10)]
    public String PasswordConfirm { get; set; }
}

You can either do the validation 'by hand' in the controller, check on the POST if the password and confirmation matches, if not add an entry to the ModelState (but that may cause code repetition and is a bit cumbersome) OR use nice IValidatableObject interface on the model:

public class Registration : IValidatableObject
{

    public String LoginName { get; set; }

    [Required]
    [StringLength(50, MinimumLength=10)]
    public String Password { get; set; }

    [Required]
    [StringLength(50, MinimumLength=10)]
    public String PasswordConfirm { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext context)
    {
        if(Password != PasswordConfirm)
            yield return new ValidationResult("Confirmation doesn't match", new[] {"PasswordConfirm"})
        //etc.
    }
}

Now with that, when you have your model bound after POST, the validation is done by simply calling ModelState.IsValid, and if it isn't valid, it returns the list of errors - including your custom errors.

Now of course you can put the DataAnnotations on the DB-model too as an additional measure, just 'in case' to avoid string truncation exceptions etc. if you somehow forgot and tried to push a longer string to the database anyway

As for the mapping, yes, after you have your model validated, at the end of the POST action you usually you map the properties from the model to either a new User instance (when adding to the DB) or to the existing one for update. You can use AutoMapper or write a naive mapper yourself using reflection - it's a relatively easy task, but it might be better to leave that as a stand-alone exercise, there is no point in reinventing the wheel.

Upvotes: 2

Vitaly Zemlyansky
Vitaly Zemlyansky

Reputation: 331

You should create your entities only in domain layer. But when you need some DataValidation Annotations for your entity, you can use MvcExtensions for this. And if you have some composite or nested entities and you want to get them as a flatten objects, you should use automapper. This will be a best practice for you!

Upvotes: 1

Related Questions