The Three Models of ASP.NET MVC Apps
We've inherited from the original MVC pattern a rather simplistic idea of what should be in the Model. In fact, in ASP.NET MVC, there are three distinct types of model: the domain model, view model and input model. As the data behind an ASP.NET MVC application becomes more complex, the more the view model and domain model may diverge.
When you use the standard project template for creating ASP.NET MVC 3 applications in Visual Studio 2010, the newly created project comes with a predefined folder named Models. The Models folder goes hand-in-hand with two other predefined folders called Views and Controllers. Models, Views, and Controllers form the triad of roles that underlie any application that conforms to the popular MVC (Model-View-Controller) design pattern.
In ASP.NET MVC, the Views and Controllers folders are not simply containers of classes; if you remove or rename Views and Controllers then your project won’t compile unless you do some (legal and documented) magic with the internal plumbing of the ASP.NET MVC runtime. Moreover, the Views folder must also be deployed as-is onto the production server. So in the end, Views and Controllers play a key role in ASP.NET MVC. What about Models?
First and foremost, the Models folder plays no decisive role in the working of an ASP.NET MVC project. You can freely remove or rename it; this won’t have any significant impact on your project. As the name itself seems to suggest, the intended role of the Models folder is acting as a central repository of model classes. Oh nice, but wait a moment! What’s a model class ultimately?
In the end, the term “model” is fairly overloaded these days and needs some qualifying attribute to be fully understood. This has to do with the evolution that the MVC pattern, and applications, underwent recently. ASP.NET MVC is just one implementation of the original MVC pattern, but some further considerations apply.
From the MVC Pattern to ASP.NET MVC
A few decades ago, when the MVC pattern was first formulated, software applications weren’t as tiered and sophisticated as they are today. The controller was responsible for just about everything, including grabbing input data from the view, orchestrating operations for producing a response and packing the response for the view. A single object model was used to manage the entire flow of data, and this object model was just called “the model.”
In the original formulation of the MVC pattern, the view and model know about each other and are connected through an observer relationship. The view writes its content to the model following users’ clicking; the controller reads input data from the model, orchestrates operations and then updates the model with computed results. Finally, the model notifies the view of occurred changes and the view reads data back and refreshes itself. The MVC pattern was formulated some 30 years. For quite some time, the relatively low level of complexity of most applications never required that the “model” got a more precise definition.
It would be ingenuous to pretend that the MVC pattern for modern ASP.NET applications is the same as it was 30 years ago. The MVC pattern was originally devised to better organize the entire application; today, instead, it is still an effective pattern but limited to the frontend of layered applications—the presentation layer.
In ASP.NET MVC, a controller is primarily the repository of presentation logic much like the code-behind class in Web and Windows Forms. The controller sets up and possibly delegates any further work required to serve the request. This role is nearly the same as in the original MVC pattern. The view grabs input data and produces the user interface. This role is also nearly the same as in the original MVC pattern. The model is in the original MVC pattern the representation of the data being worked on in the view.
This definition is way too limiting in modern layered applications such as those you may be writing with ASP.NET MVC. The concept of the “model” needs expansion.
Flavors of a Model
In general, there are three distinct types of a model. With regard to ASP.NET MVC, we can call them domain model, view model and input model. Let’s find out more.
The domain model describes the data you work with in the middle tier of the application. The domain model is expected to provide a faithful representation of the entities that populate the business domain. These entities are typically persisted by the data-access layer and consumed by services that implement business processes. The domain model is also often referred to as the entity model or simply as the data model. The domain model pushes a vision of data that is, in general, distinct from the vision of data you find in the presentation layer. (Note that the domain model “may” be the same model you work with in the presentation layer, but this should be taken as the exception rather than the rule.)
The view model describes the data being worked on in the presentation layer. Any data you present in the view (whether strings, collections, dates) finds its place in one of the properties of the view model classes. Any view model class represents the data that the controller transmits after processing operations for serving a response to the users. The view model results from the aggregation of multiple classes, one per view or page that you display.
Finally, the input model is the collection of classes that describe the input data flow that goes from the web page down to the controllers and backend. The input model faithfully represents the data being uploaded with individual HTTP requests. Usually, you manage this data as strings and read (serialized) values from collections such asQueryString and Form. Turning strings into strong types is entirely your responsibility in ASP.NET Web Forms. In ASP.NET MVC, the model-binding infrastructure does most of these chores for you and attempts to map incoming strings to matching properties of input model types. Let’s find out about the details.
Anatomy of the Input Model
In ASP.NET MVC, the input model can be envisaged as a collection of classes that model any data coming your way through an HTTP request. The input model is made of simple data transfer objects—just properties and no methods. Input model classes are used by controller methods as a way to receive posted data parameters or data being passed on the query string or HTTP headers. Here’s an example of a controller method leveraging a class in the input model:
public ActionResult Repeat(RepeatInfo model)
{
var viewModel = new RepeatViewModel {Number = number, Text = text};
return View(viewModel);
}
In the example, the Repeat method is invoked to repeat a given text a given number of times. Therefore, the pieces of information to transmit are two—a string and a number. All data is moved across the wire as strings packaged in the HTTP packet. Once the request is handed to the selected controller method, ASP.NET MVC does some magic to turn the content found in the body of the request into a fresh instance of the type declared as an argument of the controller. With an eye on the previous code snippet, the body of the HTTP request is revived into an instance of theRepeatInfo class.
The RepeatInfo class can be a plain data-transfer object defined as follows:
public class RepeatInfo
{
public String Text { get; set; }
public Int32 Number { get; set; }
}
It is one of your tasks as a developer to decide about the name and structure of input model classes. There’s some freedom as far as naming and structure of input model classes are concerned. The structure outlined above is not the only possible one. Here’s another equally valid class:
public class AnotherRepeatInfoClass
{
public String Text { get; set; }
public Long Number { get; set; }
}
As you may have guessed, RepeatInfo and AnotherRepeatInfoClass have member names in common—Text and Number. The type of these members may not be as important as their names but it is anyway required that the types are compatible as Int32 and Long. How does mapping between values in the HTTP request packet and properties declared on the input model class actually work? The model binder is the ASP.NET MVC component responsible for that.
Under the Hood of Model Binding
Any controller method executes under the control of the action invoker component. The developer decides about the signature of the controller method. For each declared parameter, the invoker obtains a model binder object. The binder’s job consists in trying to find a possible value for that parameter out of the HTTP request context. Each parameter can be bound to a specific model binder class; however, most of the time a default model binder class is used. The default model binder corresponds to the DefaultModelBinder class and it is the binder used for all parameters unless otherwise specified.
Each model binder uses its own algorithm to try to find a value for the parameters of a controller method. The default binder object makes an intensive of reflection. In particular, for each parameter the binder attempts to find a match between the parameter’s name and one of the values uploaded with the request. So if the parameter is named, say,Text then it looks in the collections of the request context (i.e., Form, QueryString) to find an entry with the same name. If a match is found then the binder attempts to convert the uploaded value (always a string) to the declared type of the parameter. If the conversion is successful, the computed value is then used in the call to the controller method; otherwise, an exception is thrown. Note that an exception is thrown on the first failure. This means that the controller method call proceeds successfully only if all of the declared parameters can be resolved by the binder. Be aware that an exception generated by the binder cannot be caught up in the controller method’s code. You need to set up a global error handler in global.asax in order to catch and handle those exceptions in some way. Note also that an exception is thrown only if the declared method parameter can’t be assigned null. For example, if you have an Int32 parameter failing in resolving the value will throw an exception because null is not a valid value for an Int32 variable. By simply changing Int32 to Nullable<Int32> in the controller method signature you fix the code.
The default binder looks into a variety of collections around the HTTP request context. The first place where it looks into is the Form collection. In this case, names of the input field are processed in a case-insensitive manner. If you have, say, an input text field named TextBox1 then a method parameter with the same name will be assigned. The content of the uploaded string will be converted to the type of the parameter if the conversion works. If a match can’t be found in Form, then the binder looks into the route parameters and then in the QueryString collection.
When you have several parameters to upload and map to variables, using primitive types and individual parameters may not be a great idea—the signature of method may become confusingly long. The nice thing about the default binder is that if the parameter type to fill in is a .NET complex type (such as the RepeatInfo class) then the binder attempts to populate the public properties based on the same name-matching algorithm. In other words, if the class to bind has a property named Text of type String (as in the example) then the property will be initialized to the value of any posted data with the same name. Put another way, if the ASP.NET form posting data has a text box with an ID of TextBox1, then the binder will try to find the match between that value and a property on the target type named TextBox1. If no match can be found, then the property on the target type will retain the default value for the type.
Custom Default Binders
The default model binder is generic enough to work most of the time. However, there might be situations in which you want to create a custom one. This happens specifically when you need to aggregate a few values being uploaded individually together to form a new object. For example, suppose that your ASP.NET form has three distinct fields for day, month and year. On the server, you’d like these three distinct fields to form a DateTime object. You can bind the individual values to distinct parameters and then perform the conversion manually in the controller method, as shown below:
public ActionResult Index(Int32 day, Int32 month, Int32 year)
In alternative, you can declare a parameter of type DateTime and bind it to a custom binder that just works to produce DateTime objects.
public ActionResult Index(
[ModelBinder(typeof(DateTimeModelBinder))] DateTime theDate)
The binding expressed here will only occur when the Index method is invoked. You can also register a binder type for all occurrences of a given type (such as DateTime in this example). Here’s the code you need:
ModelBinders.Binders[typeof(DateTime)] = new DateTimeModelBinder();
A model binder object is a class that implements the IModelBinder interface or that simply inherits fromDefaultModelBinder. For more information and examples on a custom model binder you can check my book “Programming ASP.NET MVC”, 2nd edition from Microsoft Press.
Summary
The model in MVC is frequently a misunderstood concept because it is often mistaken for the domain model. In summary, the model in the MVC pattern refers to modeling the data as seen by the view. In this regard, I’d call it the view model. In addition to the view model, you have the domain model that describes the data you work on in the backend. These two models may coincide in CRUD applications and in some other relatively simple scenarios. However, not using distinct view and domain models when the two do not coincide may be a serious architectural weak point. Finally, in ASP.NET MVC, the way in which data flows into the controller may be modeled through another bunch of ad hoc classes—the input model. However, of the three models, the input model is probably the one that you can consider optional without compromising the stability of your architecture. The input model helps in the design and understanding of data flow; but if you consider it part of the view model you’re not necessarily doing a wrong thing.
The Three Models of ASP.NET MVC Apps的更多相关文章
- How to Choose the Best Way to Pass Multiple Models in ASP.NET MVC
Snesh Prajapati, 8 Dec 2014 http://www.codeproject.com/Articles/717941/How-to-Choose-the-Best-Way-to ...
- ASP.NET MVC 5 03 - 安装MVC5并创建第一个应用程序
不知不觉 又逢年底, 穷的钞票 所剩无几. 朋友圈里 各种装逼, 抹抹眼泪 MVC 继续走起.. 本系列纯属学习笔记,如果哪里有错误或遗漏的地方,希望大家高调指出,当然,我肯定不会低调改正的.(开个小 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章 ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...
- 【ASP.NET MVC 5】第27章 Web API与单页应用程序
注:<精通ASP.NET MVC 3框架>受到了出版社和广大读者的充分肯定,这让本人深感欣慰.目前该书的第4版不日即将出版,现在又已开始第5版的翻译,这里先贴出该书的最后一章译稿,仅供大家 ...
- ASP.NET MVC掉过的坑_MVC初识及MVC应用程序结构
APS.Net MVC 浅谈[转] 来自MSDN 点击访问 MVC 理论结构 模型-视图-控制器 (MVC) 体系结构模式将应用程序分成三个主要组件:模型.视图和控制器. ASP.NET MVC 框架 ...
- 【ASP.NET MVC】jqGrid 增删改查详解
1 概述 本篇文章主要是关于JqGrid的,主要功能包括使用JqGrid增删查改,导入导出,废话不多说,直接进入正题. 2 Demo相关 2.1 Demo展示 第一部分 第二部分 2.2 ...
- 【ASP.NET MVC系列】浅谈jqGrid 在ASP.NET MVC中增删改查
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
- 在ASP.NET MVC应用中开发插件框架(中英对照)
[原文] Developing a plugin framework in ASP.NET MVC with medium trust [译文] 在ASP.NET MVC应用中开发一个插件框架 I’v ...
- ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第二章:利用模型类创建视图、控制器和数据库
在这一章中,我们将直接进入项目,并且为产品和分类添加一些基本的模型类.我们将在Entity Framework的代码优先模式下,利用这些模型类创建一个数据库.我们还将学习如何在代码中创建数据库上下文类 ...
随机推荐
- Linux环境抓包命令
有时候有些接口调用问题不好定位或者日志不够详细,那么我们往往会选择通过抓包来看详细的通讯过程.抓包有相关软件,这里说如何直接在环境里抓.假如现在我们在 Linux 下部署了 Tomcat 服务器,端口 ...
- 如何设计并使用FireMonkeyStyle
如何设计并使用FireMonkeyStyle FireMonkey使用Style来控制控件的显示方式. 每个控件都有一个StyleLookup属性,FireMonkey就是通过控件的这个属性来在当前窗 ...
- 阿里云ubuntu12.04环境下配置Apache+PHP+PHPmyadmin+MYsql
此教程中使用的相关IP等设置,在你的环境中要做相应修改. 使用之前更新apt-get,因为服务器基本上是一个裸系统 apt-get update;apt-get upgrade; 1 我们使用root ...
- 【转】性能测试工具JMeter的使用技巧
在这此对新版本jmeter的学习+温习的过程,发现了一些以前不知道的功能,所以,整理出来与大分享.本文内容如下. 1.如何使用英文界面的jmeter 2.如何使用镜像服务器 3.Jmeter分布式测试 ...
- Java-API-Package:java.sql百科
ylbtech-Java-API-Package:java.sql百科 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API.此 API 包括一个框架,凭借 ...
- Python中调用设置环境变量的bat
工作中用到一个python脚本,自动化运行某目录下的多个vc工程代码. 编译工程代码的命令如下,直接运行会失败,系统找不到devenv,我们需要添加devenv.exe所在的目录到环境变量中. os. ...
- JetBrains ReSharper Ultimate 2017.2.2激活方法
先提供两个链接, 需要直接拿去用即可 第一个: http://xidea.online 第二个: http://idea.iteblog.com/key.php (我用的这个) 方法下图: 继续: ...
- Linux学习笔记 -- 话说文件
文件基本属性 Linux系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限.为了保护系统的安全性,Linux系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定. 在Li ...
- mysql日期类型
日期类型 DATE TIME DATETIME TIMESTAMP YEAR 作用:存储用户注册时间,文章发布时间,员工入职时间,出生时间,过期时间等 YEAR YYYY(1901/2155) DAT ...
- 用yum下载rpm包(不安装)到指定目录
用yum下载rpm包(不安装)到制定目录 用yum下载rpm包 1.安装yum-downloadonly # yum install yum-downloadonly -y 2. ...