Fluent Validation + NInject + MVC - Why & How : Part 1

http://fluentvalidation.codeplex.com/

http://www.techmyview.com/post/2014/04/27/Fluent-Validation-NInject-MVC-Why-and-How-Part-1


Validation is one of the most important aspects of programming. “To err is human”. Every human being is prone to commit errors. Our application must be smart enough to catch those errors and let user know about the actions he/she needs to perform in order to proceed further. But when we say that application must have validation logic, we also consider this aspect from development and maintenance perspective. Our validation logic must be easy to integrate, easy to test and easy to change as well if the need be. Taking into consideration let’s first dig why do we need a third party validation library instead of using built in MVC validation framework.

Why to use FluentValidation.Net library instead of MVC built-in Validation Framework?

This should be first thing which needs to be answered before we explore any new framework or any alternative to existing thing? Why? Why should I go for FluentValidation when I have built-in validation framework in MVC and it is working just fine. Does FluentValidation do anything different than built-in MVC validation framework? When we understand “why” part, understanding the framework and playing with it becomes much much easier. Let’s take the question again?

Q - Does FluentValidation do anything different than built-in MVC validation framework?
The answer is NO. But it does it differently and in a much more better way. All validation frameworks do the same thing – Validate the model and make the validation errors available to the MVC framework so that the same can be displayed on the view. But how do they do it is more important.

Let’s consider the built-in MVC validation framework. It makes use of Data Annotation attributes to perform the validation. Consider following example.

public class Student
{
public int Id { get; set; } [Required]
public string Name { get; set; } [Required]
[EmailAddress]
public string Email { get; set; } [Range(2, 5)]
public int NoOfSubjects { get; set; }
}

In order to add Data Annotations we need to add namespace - System.ComponentModel.DataAnnotations;
Data Annotations are nothing but attributes over the desired properties. Required attribute indicated MVC that this field in required on the form. If user doesn’t fill this value and post back occurs then MVC framework would show an error stating that Name field is required. On the same line Email and NoOfSubjects would be validated (If you are new to Data Annotation thing then please see a running example at – MSDN). So what is wrong with above validation? Logically nothing. It would work seamlessly the way we want it. But there are few drawbacks with this approach.

  1. Models becomes dirty – “Single Responsibility” principle of the software engineering says that every class should be responsible for carrying out only that task which is assigned to it. In our case the task of Model/ViewModel is nothing but to show the data to the user. Why does it need to have validation logic as well? Some people may argue that Model’s responsibility is to show the data to the user and validation is nothing but the provision to the data correct. But in my honest opinion these two are different responsibilities. Don’t overburden our model. Let it do it’s own job only – show data to the view. If you don’t agree with me, please read second and third point and come back to first point and agree with me now.
  2. Unit Testing of the Validation logic – I won’t say that with this logic you can’t unit test the validators but it is tricky. When we cover FluentValidation you would realize how. For those who don’t know how to unit test Data Annotations they may find this post useful.
  3.  Conditional Validation – Conditional validation is not straightforward to perform using Data Annotation approach. e.g. If we wish to perform validation on Email in above example only when NoOfSubjects field is not empty then using Data Annotation it will be a difficult task. We would end up writing custom validator for that.

In order to overcome all the above three main issues of Data Annotation approach we need some framework. It would provide us to write validation logic in different class than model and still be able to bind the logic to the model with least efforts, to perform unit testing of validation rules, to perform conditional validation, to have better control over validation rules etc. If you are looking for these things then FluentValidation.Net is the answer for it. FluentValidation.Net is a very small library written in .Net which lets us write Validation rules using clean, easy to understand lambda expressions.

Fluent Validation in Action

Enough of the theory part. Now that we understood the need of FluentValidation, let’s put it in some action. We would create a simple MVC5 application and would perform validation of model/view model using fluent validation and then we would put NInject into action for dependency injection of validators. We will use Visual Studio Express 2013 for Web to build our demo project in step by step way.

Create an MVC5 application and add FluentValidation nuget package as shown below.

If you are using MVC4 or MVC3, still you can follow same steps. Just replace FluentValidation.MVC5 with MVC4 or MVC3 whichever version you are using. Click on install. It would add FluentValidation references to the project. You are all set to use FluentValidation now.

Add a class in model folder. I have added following class -

public class Student
{
public string Name { get; set; } public string Email { get; set; } public string AddressLine1 { get; set; } public string AddressLine2 { get; set; } public string City { get; set; }
}

I have added a simple plain class. Let’s add one view also to show this model.

@model FluentValidationMVC5Demo.Models.Student

@{
ViewBag.Title = "Index";
} <h2>Index</h2> @using (Html.BeginForm())
{
@Html.AntiForgeryToken() <div class="form-horizontal">
<h4>Student</h4>
<hr />
@Html.ValidationSummary(true) <div class="form-group">
@Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
</div> <div class="form-group">
@Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Email)
@Html.ValidationMessageFor(model => model.Email)
</div>
</div> <div class="form-group">
@Html.LabelFor(model => model.AddressLine1, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.AddressLine1)
@Html.ValidationMessageFor(model => model.AddressLine1)
</div>
</div> <div class="form-group">
@Html.LabelFor(model => model.AddressLine2, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.AddressLine2)
@Html.ValidationMessageFor(model => model.AddressLine2)
</div>
</div> <div class="form-group">
@Html.LabelFor(model => model.City, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.City)
@Html.ValidationMessageFor(model => model.City)
</div>
</div> <div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
} @section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
 

I have also added two action methods in HomeController.

 
[HttpGet]
public ActionResult Index()
{
Student model = new Student();
return View(model);
} [HttpPost]
public ActionResult Index(Student model)
{
return View(model);
}

I hope you are familiar with HTTPGet and HTTPPost methods. Also I assumed that you have understood the Index view. I have simple added the code to show model and submit button on the view. No validation logic yet. Let’s add validation rules for the Student class. Create a new class with the name StudentValidator. I use this convention that validator class name would be model name + Validator. You may use different convention if you wish. My validator class looks like following -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using FluentValidation; namespace FluentValidationMVC5Demo.Models
{
public class StudentValidator : AbstractValidator<Student>
{
public StudentValidator()
{ }
}
}

Note the namespace at the top – FluetValidation. We need to add this namespace in order to use the validation framework.  Now let’s understand the code. We are saying that this class would be the validator for Student class then we need to tell the Fluent Validator by inheriting the validator class with the generic class – AbstractValidaor<>. Also this generic class would accept Student as the entity to validate. So first line says that StudentValidator would server as the validator for the Student class. Simple?

Now, second thing is you just need a parameter-less constructor in this validator class. We would write all the validation rules inside this constructor. Also we would end up using lambda expression to write validation rule.

RuleFor(x => x.Name)
.NotEmpty();
.WithMessage("Please enter Name!!!");

I have written this simple rule inside the constructor. Let’s understand the line. RuleFor is the method of AbstractValidator and since we are inheriting from it we get direct access to this method inside the constructor. It takes lambda expression as input. I hope you understand lambda expression. So the code simple means that we are writing validation rule for property Name of Student class. If you press . after RuleFor(x => x.Name) you would get the whole list of validation rules available. It includes many rules viz. NotEmpty, LessThan, GreaterThan etc. You can explore those validation rules on your own. Let’s stick to NotEmpty for time being. It means that Name property is required field. And then third line says that if validation fails you have show this error message. You can explore all the validation rules and validation methods at the home page.

Now we have written validation logic also. Let’s wire it with the MVC framework. How would MVC framework know that validation rules for Student class are there in StudentValidator class? We need to tell it. Right?

I assume that you know NInject and Dependency Injection. If you are new to NInject then I suggest you to go through NInject Tutorial and NInject Home Page. We would directly proceed here assuming you having basic knowledge of Dependency Injection. Add NInject Nuget package to our application. You can add Nuget package the same we added FluentValidation or you can go to View –> Other Window –> Package Manager Console and type folllwing -

Install-Package Ninject.MVC4 -Version 3.2.0

It does the same job as that of Nuget dialogue. I told this way just to show you another way of adding Nuget references.

MVC is all plug-n-play framework. You can plug your validator framework, authentication framework etc into MVC and MVC would use this plugged framework and neglect it’s built in framework for that particular module. We are going to use this feature of MVC. We are going to override MVC’s built in validation framework. For that, first thing we have to do is we need to write some logic which would return a validator class for a given model/view model. Let’s name it as FluentValidationConfig. It would create and return specific validator given model as input. e.g. It would return StudentValidator if we give Student as input. (Don’t worry we don’t have to do anything, MVC & NInject would take care of everything).

As a next step, we would create this class under App_Start folder. Just to be consistent with MVC folder structure, we would put all config related files under App_Start folder. I have written following code in the file -

public class FluentValidationConfig : ValidatorFactoryBase
{
private IKernel kernel; public FluentValidationConfig()
{
kernel = new StandardKernel();
    }

    public void AddBinding()
{ } public override IValidator CreateInstance(Type validatorType)
{ }
}

Let’s understand the code first. If we are writing our custom validator factory overriding MVC’s built in feature then we need to implement IValidatorFactory interface. It has following definition -

public interface IValidatorFactory {
IValidator<T> GetValidator<T>();
IValidator GetValidator(Type type);
}
 
But instead of implementing this interface, FluentValidation makes us available a simpler solution by exposing ValidatorFactoryBase. It would do most of the work for us. We just to need to override CreateInstance method of this class which would return the specific validator. We have created an instance of StandardKernel in the constructor which would deal with bindings. In the AddBindigs method we would wire the validator and it’s viewmodel. It is done as follows -
 
kernel.Bind<IValidator<ViewModell>>().To<ViewModelValidator>();
 

Where ViewModel is nothing but our model and ViewModelValidator is nothing but it’s Validator. So in our case it get’s transformed as -

 
kernel.Bind<IValidator<Student>>().To<StudentValidator>();
 
Before you put this code in AddBindings method, you may need to add using for the Model namespace as Student and StudentValidaor class are in Models folder. Now last part, we need to add code in CreateInstance method.

 
return (validatorType == null) ? null : (IValidator)kernel.TryGet(validatorType);
 

The code is self explanatory. NInject would simple resolve IValidator<Student> to StudentValidator and we are done. Full code of FluentValidationConfig file is as follows -

 
using FluentValidation;
using Ninject;
using FluentValidationMVC5Demo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace FluentValidationMVC5Demo.App_Start
{
public class FluentValidationConfig : ValidatorFactoryBase
{
private IKernel kernel; public FluentValidationConfig()
{
kernel = new StandardKernel();
AddBinding();
} public void AddBinding()
{
kernel.Bind<IValidator<Student>>().To<StudentValidator>();
} public override IValidator CreateInstance(Type validatorType)
{
return (validatorType == null) ? null : (IValidator)kernel.TryGet(validatorType);
}
}
}
 

And last step, we need to inform MVC to use this validation configuration when it wants to get validator for any model. This would be done in Global.asax file. Just one line -

 
 
FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure(
provider => { provider.ValidatorFactory = new FluentValidationMVC5Demo.App_Start.FluentValidationConfig(); });
 
 

It is self explanatory again. It configures FluentValidationConfig as the default validator factory for our MVC application. Now run the application and navigate to home page and just click on submit button, we should get that “Please enter name” error. We added the validation error only for Name fields, now you try to add validation for as many fields as you want and the way you want.

 

If we look at the AddBinding method, we observe that we need to add binding for each validator to it’s model. So everytime we add new model and it’s respective validator we would have to change this function to accommodate this new model. It is difficult to maintain. Right? Don’t worry, there is a workaround for that also. NInject offers Assembly scanning. We would instruct NInject to scan entire assembly to look for all classes which inherit AbstractValidator and add the binding automatically. It can be done in following way – AddBinding function code changes to

AssemblyScanner.FindValidatorsInAssembly(Assembly.GetExecutingAssembly())
.ForEach(match => kernel.Bind(match.InterfaceType)
.To(match.ValidatorType));
 

If we put this code in AddBinding then we do not need to worry about any binding manually. Validators would be auto bound to it’s respective models. Now replace AddBinding code with above lines and see if your code works.

 

Done!!!

 

Now if you see, Our model class is clean and it’s validation logic is clean too. Validation rules are easy to write, easy to maintain and easy to change without having to change model class any way. In next article we would learn how to unit test and use conditional validators in FluentValidation.

 

I hope you enjoyed the article. Feel free to comment if you have any doubt, suggestion or any comment on this article.

Be the first to rate this post

Fluent Validation + NInject3 + MVC5的更多相关文章

  1. MVC学习系列12---验证系列之Fluent Validation

    前面两篇文章学习到了,服务端验证,和客户端的验证,但大家有没有发现,这两种验证各自都有弊端,服务器端的验证,验证的逻辑和代码的逻辑混合在一起了,如果代码量很大的话,以后维护扩展起来,就不是很方便.而客 ...

  2. .NET业务实体类验证组件Fluent Validation

    认识Fluent Vaidation. 看到NopCommerce项目中用到这个组建是如此的简单,将数据验证从业务实体类中分离出来,真是一个天才的想法,后来才知道这个东西是一个开源的轻量级验证组建. ...

  3. Fluent Validation

    .NET业务实体类验证组件Fluent Validation   认识Fluent Vaidation. 看到NopCommerce项目中用到这个组建是如此的简单,将数据验证从业务实体类中分离出来,真 ...

  4. Fluent Validation with Web Api 2

    using FluentValidation;using FluentValidation.Attributes;using System;using System.Collections.Gener ...

  5. Asp.net core 学习笔记 Fluent Validation

    之前就有在 .net 时代介绍过了. 这个 dll 也支持 .net core 而且一直有人维护. 对比 data annotation 的 validation, 我越来越觉得这个 fluent 好 ...

  6. 包介绍 - Fluent Validation (用于验证)

    Install-Package FluentValidation 如果你使用MVC5 可以使用下面的包 Install-Package FluentValidation.MVC5 例子: public ...

  7. Fluent Validation For .NET

    //.net 中数据验证,一个开源的项目,直接下载 1 using FluentValidation; public class CustomerValidator: AbstractValidato ...

  8. MVC学习系列4--@helper辅助方法和用户自定义HTML方法

    在HTML Helper,帮助类的帮助下,我们可以动态的创建HTML控件.HTML帮助类是在视图中,用来呈现HTML内容的.HTML帮助类是一个方法,它返回的是string类型的值. HTML帮助类, ...

  9. [转]NopCommerce之旅: 应用启动

    本文转自:http://www.cnblogs.com/devilsky/p/5359881.html 我的NopCommerce之旅(6): 应用启动   一.基础介绍 Global.asax 文件 ...

随机推荐

  1. ACM:图BFS,迷宫

    称号: 网络格迷宫n行m单位列格组成,每个单元格无论空间(使用1表示),无论是障碍(使用0为了表示).你的任务是找到一个动作序列最短的从开始到结束,其中UDLR同比分别增长.下一个.左.向右移动到下一 ...

  2. android应用框架构建------AppManager

    体验一些Android应用程序开发的个人总结 当我们开发应用程序.经常有非常多非常多activity,在这一刻,我们需要一个activity堆栈,以帮助管理activity的finish和start. ...

  3. hdu 3652 B-number(数字dp)

    http://acm.hdu.edu.cn/showproblem.php? pid=3652 大致题意:"B-number"即一个整数含有子串"13"且被13 ...

  4. DOM简要

    在看Js视频的时候就感觉Dom这东西太奇妙了.在这个注重用户体验的Web设计时代里.Dom是至关重要的. 它的易用性强.而且遍历简单.支持XPath. 它既然这么强大那么就来简单的介绍Dom这个东东. ...

  5. “NET网络”进行中,多管齐下的人才力挫“”粗俗

            随着互联网的迅猛发展,一些不太干净.低俗的甚至色情的内容不断浮现.不仅严重影响了我们的上网体验,也成为扰乱互联网正常秩序的罪魁祸首,部分不法内容甚至给网民造成了一定的財产损失.在这样的 ...

  6. paip.自适应网页设计 同 响应 与设计的原理的差and实践总结

    paip.自适应网页设计 同 响应 与设计的原理的差and实践总结 响应式Web设计(Responsive Web design)的理念是: 1 #-----------自适应布局VS响应式布局 2 ...

  7. Winpcap网络编程十之Winpcap实战,两台主机通过中间主机通信

    注:源码等等的我不会全然公开的,此篇文章写出来为大家的网络编程或者课程设计提供一定的思路.. 好,本次我们须要完毕的任务是: 完毕两台主机通过中间主机的数据通信(网络层) 添加基于IP地址的转发功能 ...

  8. ANDROID PAD版本号 PHONE版本号 源代码有什么 差别?

    ANDROID PAD版本号 PHONE版本号 源代码有什么 差别? 直接把frameworks/base/core/res/res/values/config.xml里面的<bool name ...

  9. hdu 3695 Computer Virus on Planet Pandora(AC自己主动机)

    题目连接:hdu 3695 Computer Virus on Planet Pandora 题目大意:给定一些病毒串,要求推断说给定串中包括几个病毒串,包括反转. 解题思路:将给定的字符串展开,然后 ...

  10. 漂浮广告代码兼容ie、firefox,多个漂浮不冲突,调用只需两行代码

    原文:漂浮广告代码兼容ie.firefox,多个漂浮不冲突,调用只需两行代码 将广告内容放在div中,设置一个id,然后用下面方法调用var adcls=new AdMove("div的id ...