What’s the Difference Between a Value Provider and Model Binder?
ASP.NET MVC 3 introduced the ability to bind an incoming JSON request to an action method parameter, which is something I wrote about before. For example, suppose you have the following class defined (keeping it really simple here):
public class ComicBook {
public string Title { get; set; }
public int IssueNumber { get; set; }
}
And you have an action method that accepts an instance of ComicBook:
[HttpPost]
public ActionResult Update(ComicBook comicBook) {
// Do something with ComicBook and return an action result
}
You can easily post a comic book to that action method using JSON.
Under the hood, ASP.NET MVC uses the DefaultModelBinder in combination with the JsonValueProviderFactory to bind that value.
A question on an internal mailing list recently asked the question (and I’m paraphrasing here), “Why not cut out the middle man (the value provider) and simply deserialize the incoming JSON request directly to the model (ComicBook in this example)?”
Great question! Let me provide a bit of background to set the stage for the answer.
Posting Content to an Action
There are a couple of different content types you can use when posting data to an action method.
application/x-www-form-urlencoded
You may not realize it, but when you submit a typical HTML form, the content type of that submission is application/x-www-form-url-encoded.
As you can see in the screenshot below from Fiddler, the contents of the form is posted as a set of name value pairs separated by ampersand characters. The name and value within each pair are separated by an equals sign.
By the time you typically interact with this data (outside of model binding), it’s in the form of a dictionary like interface via the Request.Form name value collection.
The following screenshot shows what such a request looks like using Fiddler.

When content is posted in this format, the DefaultModelBinder calls into theFormValueProvider asking for a value for each property of the model. TheFormValueProvider is a very thin abstraction over the Request.Form collection.
application/json
Another content type you can use to post data is application/json. As you might guess, this is simply JSON encoded data.
Here’s an example of a bit of JavaScript I used to post the same content as before but using JSON. Note that this particular snippet requires jQuery and a browser that natively supports the JSON.stringify method.
<script type="text/javascript">
$(function() {
var comicBook = { Title: "Groo", IssueNumber: 101 }
var comicBookJSON = JSON.stringify(comicBook);
$.ajax({
url: '/home/update',
type: 'POST',
dataType: 'json',
data: comicBookJSON,
contentType: 'application/json; charset=utf-8',
});
});
</script>
When this code executes, the following request is created.

Notice that the content is encoded as JSON rather than form url encoded.
JSON is a serialization format so it’s in theory possible that we could straight deserialize that post to a ComicBook instance. Why don’t we do that? Wouldn’t it be more efficient?
To understand why, let’s suppose we did use serialization and walk through a common scenario. Suppose someone submits the form and they enter a string instead of a number for the field IssueNumber. You’d probably expect to see the following.
![]()
Notice that the model binding was able to determine that the Title was submitted correctly, but that the IssueNumber was not.
If our model binder deserialized JSON into a ComicBook it would not be able to make that determination because serialization is an all or nothing affair. When serialization fails, all you know is that the format didn’t match the type. You don’t have access to the granular details we need to provide property level validation. So all you’d be able to show your users is an error message stating something went wrong, good luck figuring out what.
The Solution
Instead, what we really want is a way bind each property of the model one at a time so we can determine which of the fields are valid and which ones are in error. Fortunately, the DefaultModelBinder already knows how to do that when working with the dictionary-like IValueProvider interface.
So all we need to do is figure out how to expose the posted JSON encoded content via the IValueProvider interface. As I wrote before, Jonathan Carter had the bit of insight that provided the solution to this problem. He realized that you could have the JSON value provider deserialize the incoming JSON post to a dictionary. Once you have a dictionary, it’s pretty easy to implementIValueProvider and the DefaultModelBinder already knows how to bind those values to a type while providing property level validation. Score!
Value Provider Aggregation
The answer I provided only tells part of the story of why this is implemented as a value provider. There’s another aspect that was illustrated by my co-worker Levi. Sadly, for someone so gifted intellectually, he has no blog, so I’ll paraphrase his words here (with a bit of verbatim copying).
As I mentioned earlier, value providers provide an abstraction over where values actually come from. Value providers are responsible for aggregating the values that are part of the current request, e.g. from Form collection, the query string, JSON, etc. They basically say “I don’t know what a ‘FirstName’ is for or what you can do with it, but if you ask me for a ‘FirstName’ I can give you what I have.”
Model binders are responsible for querying the value providers and building up objects based on those results. They basically say “I don’t know where directly to find a ‘FirstName’, ‘LastName’, or ‘Age’, but if the value provider is willing to give them to me then I can create a Person object from them.”
Since model binders aren’t locked to individual sources (with some necessary exceptions, e.g. HttpPostedFile), they can build objects from an aggregate of sources. If your Person type looks like this:
public class Person {
int Id { get; set; }
[NonNegative]
int Age { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
And a client makes a JSON POST request to an action method (say with the url/person/edit/1234 with the following content:
{
"Age": 30,
"FirstName": "John",
"LastName": "Doe"
}
\ The DefaultModelBinder will pull the Id value from the RouteData and theAge, FirstName, and LastName values from the JSON when building up the Personobject. Afterwards, it’ll perform validation without having to know that the various values came from different sources.
Even better, if you wrote a custom Person model binder and made it agnostic as to the current IValueProvider, you’d get the correct behavior on incoming JSON requests without having to change your model binder code one tiny iota. Neither of these is possible if the model binder is hard-coded to a single provider.
TL;DR Summary
The goal of this post was to provide a bit of detail around an interesting aspect of how ASP.NET MVC turns strings sent to a web server into strongly typed objects passed into your action methods.
Going back to the original question, the answer is simply, we use a value provider for JSON to enable property level validation of the incoming post and also so that model binding can build up an object by aggregating multiple sources of data without having to know anything about those sources.
我的理解:Value Provider 聚合了当前请求的数据,并以特有方式组织数据,提供了更细粒度的请求数据管理,Model binders更加容易从Value Provider中获取想要的数据,以便组合成有意义的input model(这里区分view model,domain model)
What’s the Difference Between a Value Provider and Model Binder?的更多相关文章
- [引]ASP.NET MVC 4 Content Map
本文转自:http://msdn.microsoft.com/en-us/library/gg416514(v=vs.108).aspx The Model-View-Controller (MVC) ...
- [翻译]Orchard如何工作
Orchard一直是博主心中神一般的存在,由于水平比较菜,Orchard代码又比较复杂看了几次都不了了之了.这次下定决心要搞懂其工作原理,争取可以在自己的项目中有所应用.为了入门先到官网去学习一下相关 ...
- [ASP.NET MVC 小牛之路]09 - Controller 和 Action (1)
我们知道,在 MVC 中每个请求都会提交到 Controller 进行处理.Controller 是和请求密切相关的,它包含了对请求的逻辑处理,能对 Model 进行操作并选择 View 呈现给用户, ...
- 【ASP.NET MVC 学习笔记】- 10 Controller和Action(1)
本文参考:http://www.cnblogs.com/willick/p/3331521.html 1.继承IController接口,示例代码将当前请求的Controller和Action打印到浏 ...
- Controller 和 Action -1
https://www.cnblogs.com/willick/p/3331521.html MVC 的每个请求都会提交到 Controller 处理.Controller 包含了对请求的逻辑处理,能 ...
- Android应用程序组件Content Provider的启动过程源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6963418 通过前面的学习,我们知道在Andr ...
- [Windows Azure] Building the web role for the Windows Azure Email Service application - 3 of 5
Building the web role for the Windows Azure Email Service application - 3 of 5. This is the third tu ...
- 分享基于EF+MVC+Bootstrap的通用后台管理系统及架构
基于EF+MVC+Bootstrap构建通用后台管理系统,集成轻量级的缓存模块.日志模块.上传缩略图模块.通用配置及服务调用, 提供了OA.CRM.CMS的原型实例,适合快速构建中小型互联网及行业 ...
- 仅此一文让你明白ASP.NET MVC 之Model的呈现(仅此一文系列三)
本文目的 我们来看一个小例子,在一个ASP.NET MVC项目中创建一个控制器Home,只有一个Index: public class HomeController : Controller { pu ...
随机推荐
- 探秘VB.net中的shared与static
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/huyuyang6688/article/details/28230345 简单了解了一 ...
- C#如何动态设置屏幕分辨率
C#如何动态设置屏幕分辨率 作者:Learning hard 这篇文章主要为大家详细介绍了C#动态设置屏幕分辨率的方法,我们可以使用Screen类设置屏幕分辨率,感兴趣的小伙伴们可以参考一下 下面就不 ...
- Linux环境下安装jenkins
废话不多说,直接开始 1.从官网下载Jenkins的war包 2.下载好的War放到Tomcat的网站根目录webapps下,然后启动Tomcat. 3.打开浏览器,输入http://IP:8080/ ...
- PCB 锣板和半孔工艺的差别
PCB 锣板和半孔工艺的差别 PCB 在做模块时会用到半孔工艺,但是由于半孔是特殊工艺. 需要加费用,打板时费还不低. 下面这个图是锣板和半孔工艺的差别. https://www.amobbs.com ...
- 省略名词: 从 Please find the attached 说起
为什么是attached 首先需要解释的,为什么是叫attached: When you would like a person to reference a document attached to ...
- 基于jwt和角色的访问控制方案-从0开始
上一版本的构想:http://www.cnblogs.com/vhyc/p/7953779.html 缺陷:dubbo的filter里面不能注入mapper的bean,而且每次查询都访问数据库速度影响 ...
- golang动态调用方法
package main import ( "fmt" "reflect" ) type YourT1 struct { } func (y *YourT1) ...
- linux下用户和组相关的文件及相关管理命令
1.用户信息文件 /etc/passwd 示例root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2: ...
- 【转】Jmeter JDBC请求的问题
如何添加一个JDBC请求?本次以Orale为例. 1 From网上下载一个名为Class12.jar 的驱动包,然后放到该目录下:[否则会提示no suitable driver] 2 查看链接O ...
- 转:oracle几组重要的常见视图-v$process,v$session,v$session_wait,v$session_event
v$process 本视图包含当前系统oracle运行的所有进程信息.常被用于将oracle或服务进程的操作系统进程ID与数据库session之间建立联系.在某些情况下非常有用: 1 如果数据库瓶颈是 ...