Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asp.net mvc validation while using viewmodel

recently i've decided to use viewmodels instead of EF EntityObjects. i'm sure that there will be no problems for GET requests but i want to know what to do with create and update actions. i've read a lot of discussions and decided that i'll act int this way. but another questions appeared:

1) when i was using EF EntityObjects with annotations the validation logic was stored in one place, but if i have different viewmodels in different projects, then i'll have to duplicate validation rules. isn't it violation of the DRY principle?

2) i've read several posts about viewmodels and validation where people suggest to validate input in viewmodels and business rules in domain models but i can't realize how can i call validation that is defined in domain models if my actions have viewmodels as parameters:

public class MyDomainModel : IValidatableObject
{
    public string Title;

    // validation of business rules
}

public class MyViewModel
{
    [Required]
    public string Title;
}

public ActionResult Edit(MyViewModel item)
{
    if (ModelState.IsValid) // MyViewModel's rules are validated not MyDomainModel's
    {
        ...
}
like image 655
donRumatta Avatar asked Dec 13 '11 13:12

donRumatta


People also ask

How do I know if a model is valid or not?

The state of the submitted Model is checked using ModelState. IsValid property and if the Model is valid then the value of the Name property is set in a ViewBag object. Note: ModelState.

What exactly does ModelState IsValid do?

ModelState. IsValid indicates if it was possible to bind the incoming values from the request to the model correctly and whether any explicitly specified validation rules were broken during the model binding process.

What is the use of ModelState in MVC?

The ModelState has two purposes: to store and submit POSTed name-value pairs, and to store the validation errors associated with each value.

Which of the following are alternatives to perform model validation instead of using built in validation attributes?

Alternatives to built-in attributes If you need validation not provided by built-in attributes, you can: Create custom attributes. Implement IValidatableObject.


1 Answers

If you switch to ViewModels you should let the framework perform validation via DataAttributes in your ViewModel classes. That is just a formal check on input, then you should validate according to your business rules (sometimes with just data annotations is just impossible to cover al the scenarios), and in case of errors, add them to the modelstate.

Example:

public class MyViewModel 
{
   [Required]
   [StringLength(20)]
   [RegularExpression("whatever")]
   public string Foo { get; set; }

   [Required]
   public int Bar { get; set; }

   public bool AFlagNotModifiableButImportant { get; set; }
}

in your Post Action you can do something like:

public ActionResult Sample (MyViewModel Obj) 
{
    if (!ModelState.IsValid) {
       return View(Obj);
    }
    // Complex business logi checks in here
    MyBusinessObj BsnObj = new MyBusinessObj(Obj);
    if (!BsnObj.IsValid()) {
      ModelState.AddModelError(string.Empty, "A veery bad error");
      return View(Obj);
    }
    // Perform Heavy Business Logic which creates a new ViewModel (eg. setting the flag property in order to show something important at view level)
    MyViewModel NewOne = BsnObj.DoIt();
    // Return a view with the new Model (can be whatever you want)
    return View(NewOne);
}

Obviously I'm keeping it very simple. Following this pattern sure add a little overhead in term of code, but checks have to be done both client side (just a formal validation on input) and server side (both formal and semantic validation). I prefer to have all semantics in a business assembly, leaving formal checks to the MVC unobtrusive validation engine (just some UI sugar in my views, yes, I hate Javascript) .

Usually my Business Objects use ViewModel's properties considering them readonly (just the usefull ones to prevent bad injections) and do the dirty/heavy job.

This may not be the perfect solution for everything, but I've noticed that applying this pattern (and force other member of the team to do the same), is leading to a good codebase. Yes, we're still far from perfection, I'd like to write both semantics and formal checks just once, but that's how web is working right now.

Let me know if you need further advice or if I've completely misunderstood your question.

PS: once you choose a pattern, stick to it no matter what.

EDIT: (long comments are a no no)

In the constructor I usually apply mappings on fields I need to change, I try to consider the ViewModel properties as readonly, to avoid unwanted modifications.

My IsValid() method just holds business checks (for example, given an ID it checks for real existence in a certain table or given a username checks if he can actually access certain data).

It's just a separation between ViewModel validation (for me is just syntactic => strings are strings, integer are integer, positive numbers are >= 0, string lengths are respected, ranges are met and so on) and real business validity (semantic => a user can access some data, an object is valid in the scope of the application).

Of course the Business validation layer can also be simple (or not exists at all), I prefer to keep them separated for reusability (often my business logic is shared between an MVC application and a WPF application). It's a bit of extra work but on the long run it pays better, I can use my complex business logic everywhere. (and working with Banks, it's the greatest goal. Change logic in just one assembly, for instance to add a new check on something, and being confident that each application using that assembly will be up to date).

So definitely is a more of extrawork, but I think it's better investing a few dev hours before than wasting days lately for maintenance/evolutions.

Nowadays programming seems reduced to a go-and-forget activity, (due to reduce time/budget or simply because we do our task and then change employee), but each line that is coded, will need some kind of maintenance in the future, so it's better to keep things ordered and clean, preferring maintenance ease against developing speed.

like image 65
BigMike Avatar answered Sep 22 '22 13:09

BigMike