Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Model Validation From Database

I have a very simple Model, that needs to get validated from Database

public class UserAddress
{
    public string CityCode {get;set;}
}

CityCode can have values that are only available in my database table.

I know i can do something like.

[HttpPost]
public ActionResult Address(UserAddress model)
{
    var connection = ; // create connection
    var cityRepository = new CityRepository(connection);

    if (!cityRepository.IsValidCityCode(model.CityCode))
    {
        // Added Model error
    }
}

This seems very WET as I have to use this model at lot of placed and adding the same logic each place seems like i am not using MVC Architecture properly.

So, what is the best pattern to validate model from Database ?

NOTE: Most of the validation are single field lookup from Database, other validation may include combination of field. But right now I am happy with single field lookup validation, as long as it is DRY and is not using too much reflection it is acceptable.

NO CLIENT SIDE VALIDATION : To anyone who is answering in terms of client side validation, I do not need any such validation, most of my validation are server sided, and I need the same, please do not answer with Client Side Validation methods.


P.S. If any one can give me hint how to do a attribute based validation from Database, will be extremely greatful.

like image 855
Parimal Raj Avatar asked Jun 29 '15 07:06

Parimal Raj


People also ask

Can we do validation in MVC using data annotations?

In Asp.net MVC, we can easily apply validation to web application by using Data Annotation attribute classes to model class. Data Annotation attribute classes are present in System.

What is model validation in MVC?

Model validation is the process of checking whether the user input is suitable for model binding and if not it should provide useful error messages to the user.


4 Answers


Please check the EDIT from attached from the middle of this answer, for more elaborate and generic solution.


Following is my solution to do a simple Attribute based Validation. Create an attribute -

public class Unique : ValidationAttribute {     public Type ObjectType { get; private set; }     public Unique(Type type)     {         ObjectType = type;     }     protected override ValidationResult IsValid(object value, ValidationContext validationContext)     {         if (ObjectType == typeof(Email))         {             // Here goes the code for creating DbContext, For testing I created List<string>             // DbContext db = new DbContext();             var emails = new List<string>();             emails.Add("[email protected]");             emails.Add("[email protected]");              var email = emails.FirstOrDefault(u => u.Contains(((Email)value).EmailId));              if (String.IsNullOrEmpty(email))                 return ValidationResult.Success;             else                 return new ValidationResult("Mail already exists");         }          return new ValidationResult("Generic Validation Fail");     } } 

I created a simple model to test -

public class Person {     [Required]     [Unique(typeof(Email))]     public Email PersonEmail { get; set; }     [Required]     public GenderType Gender { get; set; } }  public class Email {     public string EmailId { get; set; } } 

Then I created following View -

@model WebApplication1.Controllers.Person @using WebApplication1.Controllers;  <script src="~/Scripts/jquery-1.10.2.min.js"></script> <script src="~/Scripts/jquery.validate.min.js"></script> <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>  @using (Html.BeginForm("CreatePersonPost", "Sale")) {     @Html.EditorFor(m => m.PersonEmail)      @Html.RadioButtonFor(m => m.Gender, GenderType.Male) @GenderType.Male.ToString()     @Html.RadioButtonFor(m => m.Gender, GenderType.Female) @GenderType.Female.ToString()     @Html.ValidationMessageFor(m => m.Gender)      <input type="submit" value="click" /> } 

Now When I enter the same Email - [email protected] and click on Submit button I can get errors in my POST action, as shown below.

enter image description here


EDIT Here goes more generic and detailed answer.


Create IValidatorCommand -

public interface IValidatorCommand {     object Input { get; set; }     CustomValidationResult Execute(); }  public class CustomValidationResult {     public bool IsValid { get; set; }     public string ErrorMessage { get; set; } } 

Lets assume we have our Repository and UnitOfWork defined in following way -

public interface IRepository<TEntity> where TEntity : class {     List<TEntity> GetAll();     TEntity FindById(object id);     TEntity FindByName(object name); }  public interface IUnitOfWork {     void Dispose();     void Save();     IRepository<TEntity> Repository<TEntity>() where TEntity : class; }  

Now lets created our own Validator Commands -

public interface IUniqueEmailCommand : IValidatorCommand { }  public interface IEmailFormatCommand : IValidatorCommand { }  public class UniqueEmail : IUniqueEmailCommand {     private readonly IUnitOfWork _unitOfWork;     public UniqueEmail(IUnitOfWork unitOfWork)     {         _unitOfWork = unitOfWork;     }     public object Input { get; set; }      public CustomValidationResult Execute()     {         // Access Repository from Unit Of work here and perform your validation based on Input         return new CustomValidationResult { IsValid = false, ErrorMessage = "Email not unique" };     } }  public class EmailFormat : IEmailFormatCommand {     private readonly IUnitOfWork _unitOfWork;     public EmailFormat(IUnitOfWork unitOfWork)     {         _unitOfWork = unitOfWork;     }     public object Input { get; set; }      public CustomValidationResult Execute()     {         // Access Repository from Unit Of work here and perform your validation based on Input         return new CustomValidationResult { IsValid = false, ErrorMessage = "Email format not matched" };     } } 

Create our Validator Factory which will give us a particular command based on Type.

public interface IValidatorFactory {     Dictionary<Type,IValidatorCommand> Commands { get; } }  public class ValidatorFactory : IValidatorFactory {     private static Dictionary<Type,IValidatorCommand> _commands = new Dictionary<Type, IValidatorCommand>();      public ValidatorFactory() { }              public Dictionary<Type, IValidatorCommand> Commands     {         get         {             return _commands;         }     }      private static void LoadCommand()     {         // Here we need to use little Dependency Injection principles and         // populate our implementations from a XML File dynamically         // at runtime. For demo, I am passing null in place of UnitOfWork         _commands.Add(typeof(IUniqueEmailCommand), new UniqueEmail(null));         _commands.Add(typeof(IEmailFormatCommand), new EmailFormat(null));     }      public static IValidatorCommand GetCommand(Type validatetype)     {         if (_commands.Count == 0)             LoadCommand();                      var command = _commands.FirstOrDefault(p => p.Key == validatetype);         return command.Value ?? null;     } } 

And the renovated Validation Attribute -

public class MyValidateAttribute : ValidationAttribute {     public Type ValidateType { get; private set; }     private IValidatorCommand _command;     public MyValidateAttribute(Type type)     {         ValidateType = type;                 }     protected override ValidationResult IsValid(object value, ValidationContext validationContext)     {         _command = ValidatorFactory.GetCommand(ValidateType);         _command.Input = value;         var result = _command.Execute();          if (result.IsValid)             return ValidationResult.Success;         else             return new ValidationResult(result.ErrorMessage);     } } 

Finally we can use our attribute as follows -

public class Person {     [Required]     [MyValidate(typeof(IUniqueEmailCommand))]     public string Email { get; set; }     [Required]     public GenderType Gender { get; set; } } 

Output as follows -

enter image description here


EDIT Detailed explanation to make this solution more generic.


Lets say I have a property Email where I need to do following validations -

  1. Format
  2. Length
  3. Unique

In that case we can create IEmailCommand inherited from IValidatorCommand. Then inherit IEmailFormatCommand, IEmailLengthCommand and IEmailUniqueCommand from IEmailCommand.

Our ValidatorFactory will hold the pool of all three command implementations in Dictionary<Type, IValidatorCommand> Commands.

Now instead of decorating our Email property with three commands, we can decorate it with IEmailCommand.

In this case, our ValidatorFactory.GetCommand() method needs to be changed. Instead of returning one command every time, it should return all matching commands for a particular type. So basically its signature should be List<IValidatorCommand> GetCommand(Type validatetype).

Now as we can get all the commands associated with a property we can loop the commands and get the Validation results in our ValidatorAttribute.

like image 119
ramiramilu Avatar answered Oct 01 '22 14:10

ramiramilu


I would have used RemoteValidation. I've found this simplest for scenarios like validations against database.

Decorate your property with Remote attribute -

[Remote("IsCityCodeValid","controller")] public string CityCode { get; set; } 

Now, "IsCityCodeValid" will be a action method, that will return JsonResult and take the property name that you want to validate as parameter and "controller" is the name of controller in which your method will be placed. Make sure that the parameter name is same as property name.

Do your validations in the method, and in case it is valid return json true and false otherwise. Simple and quick!

    public JsonResult IsCityCodeValid(string CityCode)     {         //Do you DB validations here         if (!cityRepository.IsValidCityCode(cityCode))         {             //Invalid            return Json(false, JsonRequestBehavior.AllowGet);         }         else         {                         //Valid             return Json(true, JsonRequestBehavior.AllowGet);         }     } 

And you are done!!. MVC framework will take care of the rest.

And of course based on you requirement, you can use different overloads of remote attribute. You can also include other dependent properties, defining custome error message etc. You can pass even pass the model class as parameter to Json result action method MSDN Ref.

like image 23
Yogi Avatar answered Oct 01 '22 14:10

Yogi


I think you should use a custom validation

public class UserAddress
{
    [CustomValidation(typeof(UserAddress), "ValidateCityCode")]
    public string CityCode {get;set;}
}
public static ValidationResult ValidateCityCode(string pNewName, ValidationContext pValidationContext)
{
    bool IsNotValid = true // should implement here the database validation logic
    if (IsNotValid)
        return new ValidationResult("CityCode not recognized", new List<string> { "CityCode" });
    return ValidationResult.Success;
}
like image 40
liviu mamelluc Avatar answered Oct 01 '22 12:10

liviu mamelluc


if you really want to validate from database here are some techniques you can follow 1.using System.ComponentModel.DataAnnotations add reference to class

public int StudentID { get; set; }
[StringLength(50)]
public string LastName { get; set; }
[StringLength(50)]
public string FirstName { get; set; }
public Nullable<System.DateTime> EnrollmentDate { get; set; }
[StringLength(50)]
 public string MiddleName { get; set; }

here string lenght is defined i.e 50 and datetime can be nullable etc EF Database First with ASP.NET MVC: Enhancing Data Validation

like image 37
rohit poudel Avatar answered Oct 01 '22 13:10

rohit poudel