[MVC][02]Data Validation with Entity Framework

[MVC][02]Data Validation with Entity Framework

strong-type classes generated by EF are not editable and usually stand in the way of data validation
But you can still use MetaData Class to do data validation.
contents below follows this post:[MVC][01]Using scaffold to automatically create MVC CRUD site
(hereinafter called the 'previous post')

let's start!
create another table for full test of validations and insert a new row:

CREATE TABLE [dbo].[ValidationRow]
(
	[Id] INT NOT NULL identity(1,1) PRIMARY KEY, 
	[TestRequired] NVARCHAR(50) NULL, 
    [TestStringLength] NVARCHAR(50) NULL, 
    [TestRange] NUMERIC(3) NULL, 
    [TestRegularExp] NVARCHAR(50) NULL,
	[TestCustomValidation] NVARCHAR(50) NULL

	
)

insert into ValidationRow
(TestRequired, TestStringLength, TestRange, TestRegularExp, TestCustomValidation)
VALUES
(N'I am required',N'Measure length of this string',3,N'I,am,comma,separated',N'This custom content!')

using scaffold to create CRUD method like previous post and execute the project:
everything on the screen is just like we expected, nothing special

check the of namespace of ValidationRow.cs in your ado.net entity model(.edmx):
namespace MVC_5.Models

add a "MetaData" folder under "Models" folder in the project:
may be you prefer "SuperMetaData", it is up to you. :D

add a ValidationRow.cs under MetaData folder, then edit the class as follows(read the comments in my code):

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

//change name space
//namespace MVC_5.Models.MetaData
namespace MVC_5.Models
{
    //add this using
    using System.ComponentModel.DataAnnotations;

    //your can change"ValidationRowMetadata" to any other name you want
    [MetadataType(typeof(ValidationRowMetadata))]
    //key word: partial class
    public partial class ValidationRow
    {
        private class ValidationRowMetadata
        {
            [Required]
            [Display(Name = "Column for 'Required' test!")]
            public virtual string TestRequired { get; set; }

            [StringLength(3,ErrorMessage = "string length do not exceed 3!")]            
            public virtual string TestStringLength { get; set; }

            [Range(1,3, ErrorMessage = "Range must between 1  and 3!")]
            public virtual decimal TestRange { get; set; }
            
            [RegularExpression("\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*",
                ErrorMessage = "This is not a email!")]
            public virtual string TestRegularExp { get; set; }
        }
    }
}

the code above add validations:
1. Required
2. StringLength
3. Range
4. RegularExpression


execute the project and you'll see the data validation work like a charm:


there are still lots of other types of data validations(ex:MaxLengh, CreditCard, phone....) at in page System.ComponentModel.DataAnnotations Namespace, refer to it when you need more basic validations.

custom validation:
this is not as easy to use as other basic attributes, you have to implement both server and client side validation. but not that difficult, no worry
let's start!
this custom validation limits the wordcount of the input, ex:there are 4 words in "this is an apple" and you can limit the max word count to 2, then "this is an apple" will be illegal

add an empty controller class "ValidationsController.cs":business check logic will be here for server side and client side.


content of this controller class:(read the comments in the code)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVC_5.Controllers
{
    public class ValidationsController : Controller
    {
        /// <summary>
        /// Your parameter name "TestCustomValidation" must be the same as 
        /// MetaData Class's "Remote" Attribute's AdditionalFields 
        /// </summary>
        /// <param name="TestCustomValidation"></param>
        /// <returns></returns>
        public JsonResult CheckWordCount(string TestCustomValidation)
        {
            int maxWordCount = 3;
            string errMsg = "words count do not exceed(space separated)" + maxWordCount + "!";            
            if (TestCustomValidation != null)
            {
                var wordCount = TestCustomValidation.ToString().Split(' ').Length;
                if (wordCount > maxWordCount)
                {
                    return Json(errMsg, JsonRequestBehavior.AllowGet);
                }
                else
                {
                    return Json(true, JsonRequestBehavior.AllowGet);
                }
            }
            else
            {
                return Json(errMsg, JsonRequestBehavior.AllowGet);
            }


        }
    }
}

add an "Attributes" folder under "Models" folder, meanwhile add a class which is a custom attribute class, "MaxWordsAttribute.cs":

content of "MaxWordsAttribute.cs":(read the comments in the code)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
//add this using for server side validations
using System.ComponentModel.DataAnnotations;
//add this using for calling controller's business check logic
using MVC_5.Controllers;
//add this using for JSonResult
using System.Web.Mvc;

namespace MVC_5.Models.Attributes
{
    public class MaxWordsAttribute : ValidationAttribute
    {
        public MaxWordsAttribute()
        {
            //we do not set the max word count here
            //the setting will be move to controller class
        }

        

        protected override ValidationResult IsValid(
            object value,
            ValidationContext validationContext)
        {
            if (value == null)
            {
                return new ValidationResult(
                        FormatErrorMessage(validationContext.DisplayName));
            }

            //call controller's business check logic 
            JsonResult checkWordCountResult =
                new ValidationsController().CheckWordCount(value.ToString());
            //convert JsonResult to string
            string checkWordCountResultString =
                new System.Web.Script.Serialization.JavaScriptSerializer()
                .Serialize(checkWordCountResult.Data).ToLower();
            if (checkWordCountResultString == "true")
            {
                return ValidationResult.Success;
            }
            else
            {
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
        }
    }
}

add using and 
set "TestCustomValidation" column in your MetaData Class "ValidationRow.cs":

//using this for server side validation attribute "MaxWords"
using MVC_5.Models.Attributes;
//using this for client side validation attribute "Remote"
using System.Web.Mvc;

[Remote("CheckWordCount", "Validations", AdditionalFields = "TestCustomValidation")]
[MaxWords]
public virtual string TestCustomValidation { get; set; }

execute the project then done:
you will see the validation, both server side and clients side,  works like charm


cheers!

Download this project(I use visual studio 2017 community to complete this project)

References:
【Asp.Net MVC】Model Validation:進階應用的遠端 API 驗證(Remote validation)
https://dotblogs.com.tw/steventsai/2016/12/02/095135
how to use multiple AdditionalFields in remote validation - asp.net mvc
https://stackoverflow.com/questions/8046841/how-to-use-multiple-additionalfields-in-remote-validation-asp-net-mvc?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
Custom Data Annotation Validation In MVC
https://www.c-sharpcorner.com/article/custom-data-annotation-validation-in-mvc/
ASP.NET email validator regex
https://stackoverflow.com/questions/1710505/asp-net-email-validator-regex?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
[ASP.Net MVC] 自訂Custom Validation Attribute進行Model驗證
https://dotblogs.com.tw/wasichris/2015/04/25/151150
Implement Data Validation in MVC
http://www.tutorialsteacher.com/mvc/implement-validation-in-asp.net-mvc
Enable client side validation in MVC
http://www.tutorialsteacher.com/articles/enable-client-side-valiation-in-mvc
ASP.NET MVC: Custom Validation by DataAnnotation
https://stackoverflow.com/questions/16100300/asp-net-mvc-custom-validation-by-dataannotation?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
[.NET][EF]設定MetaData Class來保留edmx的屬性
https://dotblogs.com.tw/kevinya/2016/03/02/175927