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 文件 ...
随机推荐
- 【Android进阶】使用Andbase快速开发框架实现常见侧滑栏和滑动标签页组合效果
最近闲来无事,在网上寻找源代码看,突然发现了一个国内技术牛人开发的快速开发框架Andbase,花了一天时间研究了下源码和怎么使用,现将开发常见的侧滑栏和滑动标签页组合效果的使用介绍个大家,希望可以减少 ...
- HDU 1085-Holding Bin-Laden Captive!(生成功能)
Holding Bin-Laden Captive! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Ja ...
- 【原创】构建高性能ASP.NET站点之三 细节决定成败
原文:[原创]构建高性能ASP.NET站点之三 细节决定成败 构建高性能ASP.NET站点之三 细节决定成败 前言:曾经就因为一个小小的疏忽,从而导致了服务器崩溃了,后来才发现:原来就是因为一个循环而 ...
- Chrome 扩展 最近的历史 HistoryBar v1.1
说明 以前用过一段时间傲游浏览器,渐渐的习惯了它的鼠标手势和一些细微的人性化的功能.比方地址栏左边的"近期訪问的页面"button.能够方便的找到近期 20 条历史记录. wate ...
- Eclipse+超快速的模拟器Genymotion开展Android申请书(第一步:安装和配置Genymotion)
一.安装和配置Genymotion (1)因为Eclipse自带SDK模拟器,慢启动,别说 今天给大家介绍一个更快速的模拟器Genymotion (2)第一次去Genymotion在官方网站上注册一个 ...
- UVa 10190 - Divide, But Not Quite Conquer!
称号:给你第一个任期的等比数列和倒数公比,最后一个条目假定1这一系列的输出,否则输出Boring!. 分析:数学.递减的.所以公比的倒数一定要大于1.即m > 1. 然后在附加一个条件n &g ...
- Putty设置自己主动两次登录
有时你想登录到serverA,但serverA白名单,你刚刚从山寨机B登录了,所以每次你要登录到serverA.您必须先登录到山寨机B.然后登录到serverA. 我们能够用Putty的local p ...
- Java并发专题 带返回结果的批量任务执行 CompletionService ExecutorService.invokeAll(转)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/27250059 一般情况下,我们使用Runnable作为基本的任务表示形式,但是R ...
- tomcat内存溢出,改动设置
问题描写叙述: 1. java.lang.OutOfMemoryError: Java heap space JVM堆的设置是指java程序执行过程中JVM能够调配使用的内存空间的设置.JVM在启动的 ...
- STL 源代码分析 算法 stl_algo.h -- pre_permutation
本文senlie原版的,转载请保留此地址:http://blog.csdn.net/zhengsenlie pre_permutation ------------------------------ ...