12 June 2012  by Dino Esposito
 

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的更多相关文章

  1. 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 ...

  2. ASP.NET MVC 5 03 - 安装MVC5并创建第一个应用程序

    不知不觉 又逢年底, 穷的钞票 所剩无几. 朋友圈里 各种装逼, 抹抹眼泪 MVC 继续走起.. 本系列纯属学习笔记,如果哪里有错误或遗漏的地方,希望大家高调指出,当然,我肯定不会低调改正的.(开个小 ...

  3. 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

  4. 【ASP.NET MVC 5】第27章 Web API与单页应用程序

    注:<精通ASP.NET MVC 3框架>受到了出版社和广大读者的充分肯定,这让本人深感欣慰.目前该书的第4版不日即将出版,现在又已开始第5版的翻译,这里先贴出该书的最后一章译稿,仅供大家 ...

  5. ASP.NET MVC掉过的坑_MVC初识及MVC应用程序结构

    APS.Net MVC 浅谈[转] 来自MSDN 点击访问 MVC 理论结构 模型-视图-控制器 (MVC) 体系结构模式将应用程序分成三个主要组件:模型.视图和控制器. ASP.NET MVC 框架 ...

  6. 【ASP.NET MVC】jqGrid 增删改查详解

    1   概述 本篇文章主要是关于JqGrid的,主要功能包括使用JqGrid增删查改,导入导出,废话不多说,直接进入正题. 2   Demo相关 2.1   Demo展示 第一部分 第二部分 2.2 ...

  7. 【ASP.NET MVC系列】浅谈jqGrid 在ASP.NET MVC中增删改查

    ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...

  8. 在ASP.NET MVC应用中开发插件框架(中英对照)

    [原文] Developing a plugin framework in ASP.NET MVC with medium trust [译文] 在ASP.NET MVC应用中开发一个插件框架 I’v ...

  9. ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第二章:利用模型类创建视图、控制器和数据库

    在这一章中,我们将直接进入项目,并且为产品和分类添加一些基本的模型类.我们将在Entity Framework的代码优先模式下,利用这些模型类创建一个数据库.我们还将学习如何在代码中创建数据库上下文类 ...

随机推荐

  1. hadoop文件IO

    InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符.它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集. Input ...

  2. bzoj 1085 [SCOI2005]骑士精神——IDA*

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1085 迭代加深搜索. 估价函数是为了预计步数来剪枝,所以要优于实际步数. 没错,不是为了确定 ...

  3. 洛谷 1099 ( bzoj 1999 ) [Noip2007]Core树网的核

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1999 <算法竞赛进阶指南>346页.https://www.cnblogs.co ...

  4. DataSnap侦听端口动态设置问题

    使用DataSnap做服务器,端口设置为可配置,然后在程序中动态指定: procedure TscUPower.DataModuleCreate(Sender: TObject); begin dst ...

  5. DELPHI中使用UNIDAC连接ORACLE数据库

    DELPHI中使用UNIDAC连接ORACLE数据库   最近在DELPHI中使用到UNIDAC连接到oracle数据库,这样可以不要安装oracle客户端,比较方便使用:所以简单学习了一下,主要是用 ...

  6. .com .cn .org .edu等域名的意义

    在开发的时候遇到了.org的域名,后来就去查了一下,原来这种域名是非盈利组织或者协会的标志 比如: https://getcomposer.org/ https://packagist.org/ ht ...

  7. Tair 分布式K-V存储方案

    tair 是淘宝的一个开源项目,它是一个分布式的key/value结构数据的解决方案. 作为一个分布式系统,Tair由一个中心控制节点(config server)和一系列的服务节点(data ser ...

  8. UiAutomator viewer 在tools下启动、资源jar包在platforms下(如android22)

    1.UiAutomator viewer 在tools下启动. 2.资源jar包在platforms下(如android22):android.jar/ uiautomator.jar

  9. Windows10 官方原版镜像下载途径 Label:win10解决方案

    https://www.microsoft.com/en-gb/software-download/windows10ISO 设置浏览标签为手机以避免跳转,下载即可  或者手机打开该网址,获取下载链接 ...

  10. __CLASS__

    <?php class base_class { function say_a() { echo "'a' - said the " . __CLASS__ . " ...