Fluent Validation + NInject3 + MVC5
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.
- 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.
- 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.
- 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);
}
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>();
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.
Fluent Validation + NInject3 + MVC5的更多相关文章
- MVC学习系列12---验证系列之Fluent Validation
前面两篇文章学习到了,服务端验证,和客户端的验证,但大家有没有发现,这两种验证各自都有弊端,服务器端的验证,验证的逻辑和代码的逻辑混合在一起了,如果代码量很大的话,以后维护扩展起来,就不是很方便.而客 ...
- .NET业务实体类验证组件Fluent Validation
认识Fluent Vaidation. 看到NopCommerce项目中用到这个组建是如此的简单,将数据验证从业务实体类中分离出来,真是一个天才的想法,后来才知道这个东西是一个开源的轻量级验证组建. ...
- Fluent Validation
.NET业务实体类验证组件Fluent Validation 认识Fluent Vaidation. 看到NopCommerce项目中用到这个组建是如此的简单,将数据验证从业务实体类中分离出来,真 ...
- Fluent Validation with Web Api 2
using FluentValidation;using FluentValidation.Attributes;using System;using System.Collections.Gener ...
- Asp.net core 学习笔记 Fluent Validation
之前就有在 .net 时代介绍过了. 这个 dll 也支持 .net core 而且一直有人维护. 对比 data annotation 的 validation, 我越来越觉得这个 fluent 好 ...
- 包介绍 - Fluent Validation (用于验证)
Install-Package FluentValidation 如果你使用MVC5 可以使用下面的包 Install-Package FluentValidation.MVC5 例子: public ...
- Fluent Validation For .NET
//.net 中数据验证,一个开源的项目,直接下载 1 using FluentValidation; public class CustomerValidator: AbstractValidato ...
- MVC学习系列4--@helper辅助方法和用户自定义HTML方法
在HTML Helper,帮助类的帮助下,我们可以动态的创建HTML控件.HTML帮助类是在视图中,用来呈现HTML内容的.HTML帮助类是一个方法,它返回的是string类型的值. HTML帮助类, ...
- [转]NopCommerce之旅: 应用启动
本文转自:http://www.cnblogs.com/devilsky/p/5359881.html 我的NopCommerce之旅(6): 应用启动 一.基础介绍 Global.asax 文件 ...
随机推荐
- Xcode_6.3_beta_4 官方 下载地址
http://adcdownload.apple.com//Developer_Tools/Xcode_6.3_beta_4/Xcode_6.3_beta_4.dmg
- 条形码/二维码之开源利器ZXing图文介绍(转)
继前面介绍的一个日本开源软件(该软件只能实现QRCode)原文: Java实现二维码QRCode的编码和解码(http://sjsky.iteye.com/blog/1136934 ),今发现又一优秀 ...
- Oracle 11G DataGuard生产环境又一次启动具体过程
场景,重新启动数据库,不重新启动linux系统,所以不用考虑监听程序,#linux输入lsnrctl start1 数据库关闭1.1 关闭主库SHUTDOWN IMMEDIATE; SQL> ...
- mysql声明摘要
前一段时间,和学生参加该项目的最终完成,主要的项目是做一个报告,它涉及到很多sql声明,因此,采取下一个汇总. 一.基金会 1.数据库相关的命令 a>.创建数据库 CREATE DATABASE ...
- C++ do while 0 使用和含义
/* do while 0 的使用方法和意义 */ //近期在非常多代码里都看到do while 0的身影. 乍一看,这不是没有做不论什么事情吗?为什么还要这样写.难道这是多此一举的吗?当然不是. / ...
- Go如何发送广播包
发送网络数据包成三种方式,每间单播.组播.广播. 广播通俗地讲,就是让你的机器发送的数据包能够被同一个网络内的全部主机都接收到. 在解说怎样发送广播包之前.先来看看跟广播有关的知识: 我们都知道IP地 ...
- iOS中通讯录电话号码空格问题
今天在读取通讯录的时候,读取到的手机号码格式为* (***) ***-****的,乍看下,数字中间有空格."-".(.)的非数字字符. 然后我就打算替换这些非数字字符,结果替换完, ...
- Sizzle.selectors.relative [ 源代码分析 ]
1 jQuery 对象Sizzle.selectors.relative中存放了块间关系符和相应的块间关系过滤函数,称为"块间关系过滤函数集" 块间关系符共同拥有4种,其含义和过滤 ...
- PLSQL:[1]plsql中文乱码,显示问号
PLSQL运行sql语句,不识别中文.输出的中文标题显示成问号?? ?? 工具/原料 PLSQL Developer 9 方法/步骤 1 登陆plsql,运行sql语句.输出的中文标题显示成问号??? ...
- Android MenuItem 设置文本颜色-TextColor设置
前面一直在寻找 MenuItem文字颜色设置. 我发现API唯一的背景颜色设置. .. 因此,找到下面的方法.在OverFlow看到. 在onCreateOptionsMenu一下. 使MenuIte ...