MVC and Linq2Sql – Adding validation

MVC supports a lot of ‘good practice’ such as dependency injection, unit testing and mocking. These things all have their place, but for some small ad-hoc coding all of that isnt’s needed. Additionaly, people crossing over to MVC get confronted in most books and documentation with a ton of good advice that at best obfuscates what is going on in the core. This post is a part of a series of posts for people crossing over from webforms to mvc to give a few starting concepts.

Data annotation with Linq2Sql and MVC

If you write your own classes you can annotate them to set validation rules right from the class. When adding CRUD pages later on, your validation rules will be automaticaly implemted, saving you a ton of boilerplate work. So, we want to use that with linq2sql too !

Linq2Sql luckily generates partial classes, so we can hook into those to make the magic work. Since we cannot simply add the same field names again (since they are already defined in the linq2sql classes themselves, we will use an empty class and add annotation rules there.

Below you see our empty partial class, that has to have the same name as your linq2sql class (aka table) name. In this example the name is “missions”. The first section adds an empty partial class, which points to our validation class for validation rules. The second part defines the validation rule for the class as a while, thus taking effect for the linq2sql class property’s.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace SomeProject.Models
{
    /// <summary>
    /// Empty partial class to connect to the linq2sql partial class.
    /// The metadata attribute defines the class to use for validation
    /// rules.
    /// </summary>
    [MetadataType(typeof(MissionsValidation))]
    public partial class Missions
    {
    }

    /// <summary>
    /// Validation rules. Since these will be applied to the combined
    /// class from the linq2sql definition and the definition below
    /// we can now apply validation attributes directly to any
    /// property we want.
    /// </summary>
    public class MissionsValidation
    {
        [StringLength(250, MinimumLength = 3)]
        [Required]
        public string Mission { get; set; }
    }
}

If you are using the ASP.NET MVC template bundled with some Visual Studio versions you may find that while you can validate serverside, the client side magic will not kick in. This is because the jquery-validation plugin while installed, os not enabled by defaullt. Look up the _Layout.cshtml shared code en include jqueryval bundle in it, like this:

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)

There is one more or less obvious downside to this system. If you change tables and fields in your database you can quickly regenerate the linq2sql data either from the Visual Studio environment or even faster with makemetal. You will manualy have to edit your partial classes with the validation attributes to stay in sync.

Validation Examples

//
       // String validation, required field
       [Required(ErrorMessage = "Required field")]
       public string Name { get; set; }

       //
       // String validation, require a minimum length of 5, and a maximum
       // length of 50 characters
       [Required(ErrorMessage = "Required field")]
       [StringLength(50, MinimumLength = 5)]
       public string Adres { get; set; }

       //
       // Email validation, required and must be valid. Leave
       // the required tag of to make the field optional
       [Required(ErrorMessage = "Email missing")]
       [EmailAddress(ErrorMessage = "Email invalid")]
       public string Email { get; set; }
       
       //
       // Require an integer value, in teh range 1-10
       [Required(ErrorMessage = "SomeNumber is missing")]
       [Range(1,10)]
       public int SomeNumber { get; set; }
       
       //
       //
       [Required(ErrorMessage = "Some cash is missing")]
       [Range(typeof(double),"1","100")]
       public Decimal SomeCash { get; set; }