The Three Models of ASP.NET MVC Apps

12 June 2012  by Dino Esposito

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.

时间: 2024-08-05 19:35:59

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-Pass-Multiple-Models Introduction In this article, we will discuss how to choose the most suitable way to pass multiple models from controller to vie

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

不知不觉 又逢年底, 穷的钞票 所剩无几. 朋友圈里 各种装逼, 抹抹眼泪 MVC 继续走起.. 本系列纯属学习笔记,如果哪里有错误或遗漏的地方,希望大家高调指出,当然,我肯定不会低调改正的.(开个小玩笑.哈哈.) 参考书籍:<ASP.NET MVC 4 Web编程>.<ASP.NET MVC 4 高级编程>.<ASP.NET MVC 5 高级编程>.<C#高级编程(第8版)>.<使用ASP.NET MVC开发企业及应用>.度娘谷歌等. -= 安

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

APS.Net MVC 浅谈[转] 来自MSDN 点击访问 MVC 理论结构 模型-视图-控制器 (MVC) 体系结构模式将应用程序分成三个主要组件:模型.视图和控制器. ASP.NET MVC 框架提供用于创建 Web 应用程序的 ASP.NET Web 窗体模式的替代模式. ASP.NET MVC 框架是一个可测试性非常高的轻型演示框架,(与基于 Web 窗体的应用程序一样)它集成了现有的 ASP.NET 功能,如母版页和基于成员资格的身份验证. MVC 框架在 System.Web.Mvc

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

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架,它支持3种不同的技术来创建websites(网站)和Web应用:他们分别是,Web Pages,Web Forms,和MVC.虽然MVC是一种非常流行的,有完整的用于软件开发模式理论的技术,但它在ASP.NET中却是一种新的技术. 目前最新的版本是2012年发布的ASP.NET MVC4.自从2008年发布

ASP.NET MVC 使用Remote特性实现远程属性验证

RemoteAttribute是asp.net mvc 的一个验证特性,它位于System.Web.Mvc命名空间 下面通过例子来说明 很多系统中都有会员这个功能,会员在前台注册时,用户名不能与现有的用户名重复,还要求输入手机号码去注册,同时手机号码也需要验证是否重复,下面是实体类 /// <summary> /// 会员 /// </summary> public class Member { public int Id { get; set; } [Required(Error

ASP.NET MVC 4入门

一.MVC设计模式将Web应用分解成三个部分:模型(Models).试图(Views)和控制器(Controllers),这三部分分别完成不同的功能以实现Web应用. 视图(View)代表用户交互界面,对于Web应用来说,可以概括为HTML界面,但有可能为XHTML.XML和Applet.MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以及用户的请求,不包括在视图上的业务流程的处理.业务流程的处理交予模型(Model)处理. 模型(Model)就是业务流程/状态的处理以及业务规则的制定

CRUD Operations In ASP.NET MVC 5 Using ADO.NET

Background After awesome response of an published by me in the year 2013: Insert, Update, Delete In GridView Using ASP.Net C#. It now has more than 140 K views, therefore to help beginners I decided to rewrite the article i with stepbystep approach u

ASP.NET MVC轻教程 Step By Step 6——改进表单

上一节我们使用原始的HTML表单来完成留言功能,但是ASP.NET MVC提供了丰富的HTML辅助方法来帮助我们构建更简洁优雅的表单. Step 1. 修改Form标签 首先,我们可以使用Html.BeginForm来创建form标签.这样我们可以将原来的表单改成如下代码. @{Html.BeginForm("Save", "Home"); } <label for="nickname">昵称</label> <in

ASP.NET MVC验证框架中关于属性标记的通用扩展方法

http://www.cnblogs.com/wlb/archive/2009/12/01/1614209.html 之前写过一篇文章<ASP.NET MVC中的验证>,唯一的遗憾就是在使用Data Annotation Validators方式验证的时候,如果数据库是Entityframework等自动生成的文件,就没有办法使用扩展属性标记进行标记.现在已经开始有了一些其它的Asp.net MVC 验证框架,使用上跟Data Annotation Validators差不太多,但是普遍有这样