Models and Model Binders in MVC Applications
In the ASP.NET MVC framework, the model is the part of the application that is responsible for the core application or business logic. Model objects typically access data from a persistent store, such as SQL Server, and perform the business logic on that data. Models are application specific, and therefore the ASP.NET MVC framework puts no restrictions on the kinds of model objects you can build. For instance, you can use ADO.NET DataSet or DataReader objects, or you can use a custom set of domain objects. You can also use a combination of object types to work with data.
The model is not a specific class or interface. A class is part of the model not because it implements a certain interface or derives from a certain base class. Instead, a class is part of the model because of the role that the class plays in the ASP.NET MVC application, and where the class is located in the folder structure of the application. A model class in an ASP.NET MVC application does not directly handle input from the browser, nor does it generate HTML output to the browser.
Defining the Model
Model objects are the parts of the application that implement the domain logic, also known as business logic. Domain logic handles the data that is passed between the database and the UI. For example, in an inventory system, the model keeps track of the items in storage and the logic to determine whether an item is in stock. In addition, the model would be the part of the application that updates the database when an item is sold and shipped out of the warehouse. Often, the model also stores and retrieves model state in a database.
Integrating the Model and Controller
The recommended folder to put model classes in is the Models folder that is provided by Visual Studio in the ASP.NET MVC application template. However, it is also typical to put model classes in a separate assembly, so that you can reuse these classes in different applications.
Using model classes from a controller typically consists of instantiating the model classes in controller actions, calling methods of model objects, and extracting the appropriate data from these objects to display in views. This is the recommended approach for implementing actions. It also maintains separation between the logical elements of the application, which makes it easier to test the application logic without having to test it through the user interface.
The following example shows a simple model class that represents a person.
Public Class Person
Private _Id As Integer
Public Property Id() As Integer
Get
Return _Id
End Get
Set(ByVal value As Integer)
_Id = value
End Set
End Property
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Age As Integer
Public Property Age() As Integer
Get
Return _Age
End Get
Set(ByVal value As Integer)
_Age = value
End Set
End Property
Private _Street As String
Public Property Street() As String
Get
Return _Street
End Get
Set(ByVal value As String)
_Street = value
End Set
End Property
Private _City As String
Public Property City() As String
Get
Return _City
End Get
Set(ByVal value As String)
_City = value
End Set
End Property
Private _State As String
Public Property State() As String
Get
Return _State
End Get
Set(ByVal value As String)
_State = value
End Set
End Property
Private _Zipcode As Integer
Public Property Zipcode() As Integer
Get
Return _Zipcode
End Get
Set(ByVal value As Integer)
_Zipcode = value
End Set
End Property
End Class
Model Binders
A model binder in MVC provides a simple way to map posted form values to a .NET Framework type and pass the type to an action method as a parameter. Binders also give you control over the deserialization of types that are passed to action methods. Model binders are like type converters, because they can convert HTTP requests into objects that are passed to an action method. However, they also have information about the current controller context.
A model binder lets you associate a class that implements the IModelBinder interface with an action-method parameter or with a type. The IModelBinder interface contains a GetValue method that the framework calls in order to retrieve the value of a specified parameter or type. The DefaultModelBinder class works with most .NET Framework types, including arrays and IList, ICollection, and IDictionary objects.
Example of Model Binding
The following example shows how to implement simple model binding. The model used in this example is the Person class that was defined in a previous example. This example includes a PersonController class and an Index, a Create, and a Details view. The PersonController class creates a list for storing Person objects. The Index view displays the Id and Name properties for each Person object in the list. The Create view enables the user to enter the information for a person. The Details view displays all the information for a selected person.
The following example shows the PersonController class:
Public Class PersonController
Inherits System.Web.Mvc.Controller
Shared people As New List(Of Person)()
'
' GET: /Person/
Function Index() As ActionResult
Return View(people)
End Function
'
' GET: /Person/Details/5
Function Details(ByVal person As Person) As ActionResult
Return View(person)
End Function
'
' GET: /Person/Create
Function Create() As ActionResult
Return View()
End Function
'
' POST: /Person/Create
<AcceptVerbs(HttpVerbs.Post)> _
Function Create(ByVal person As Person) As ActionResult
If Not ModelState.IsValid Then
Return View("Create", person)
End If
people.Add(person)
Return RedirectToAction("Index")
End Function
End Class
public class PersonController : Controller
{
static List<Person> people = new List<Person>();
//
// GET: /Person/
public ActionResult Index()
{
return View(people);
}
//
// GET: /Person/Details/5
public ActionResult Details(Person person)
{
return View(person);
}
//
// GET: /Person/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Person/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person person)
{
if (!ModelState.IsValid)
{
return View("Create", person);
}
people.Add(person);
return RedirectToAction("Index");
}
}
The following example shows the Index view:
<h2>Index</h2>
<p>
<%=Html.ActionLink("Create New", "Create")%>
</p>
<table>
<tr>
<th></th>
<th>
Id
</th>
<th>
Name
</th>
</tr>
<% For Each person In Model%>
<tr>
<td>
<%=Html.ActionLink("Details", "Details", person)%>
</td>
<td>
<%=Html.Encode(person.Id)%>
</td>
<td>
<%=Html.Encode(person.Name)%>
</td>
</tr>
<% Next%>
</table>
<h2>Index</h2>
<table>
<tr>
<th></th>
<th>
Id
</th>
<th>
Name
</th>
</tr>
<% foreach (var person in Model) { %>
<tr>
<td>
<%= Html.ActionLink("Details", "Details", person )%>
</td>
<td>
<%= Html.Encode(person.Id) %>
</td>
<td>
<%= Html.Encode(person.Name) %>
</td>
</tr>
<% } %>
</table>
<p>
<%= Html.ActionLink("Create New", "Create") %>
</p>
The following example shows the Create view:
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% Using Html.BeginForm()%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Id">Id:</label>
<%= Html.TextBox("Id") %>
<%= Html.ValidationMessage("Id", "*") %>
</p>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name") %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Age">Age:</label>
<%= Html.TextBox("Age") %>
<%= Html.ValidationMessage("Age", "*") %>
</p>
<p>
<label for="Street">Street:</label>
<%= Html.TextBox("Street") %>
<%= Html.ValidationMessage("Street", "*") %>
</p>
<p>
<label for="City">City:</label>
<%= Html.TextBox("City") %>
<%= Html.ValidationMessage("City", "*") %>
</p>
<p>
<label for="State">State:</label>
<%= Html.TextBox("State") %>
<%= Html.ValidationMessage("State", "*") %>
</p>
<p>
<label for="Zipcode">Zipcode:</label>
<%= Html.TextBox("Zipcode") %>
<%= Html.ValidationMessage("Zipcode", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% End Using %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Id">Id:</label>
<%= Html.TextBox("Id") %>
<%= Html.ValidationMessage("Id", "*") %>
</p>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name") %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Age">Age:</label>
<%= Html.TextBox("Age") %>
<%= Html.ValidationMessage("Age", "*") %>
</p>
<p>
<label for="Street">Street:</label>
<%= Html.TextBox("Street") %>
<%= Html.ValidationMessage("Street", "*") %>
</p>
<p>
<label for="City">City:</label>
<%= Html.TextBox("City") %>
<%= Html.ValidationMessage("City", "*") %>
</p>
<p>
<label for="State">State:</label>
<%= Html.TextBox("State") %>
<%= Html.ValidationMessage("State", "*") %>
</p>
<p>
<label for="Zipcode">Zipcode:</label>
<%= Html.TextBox("Zipcode") %>
<%= Html.ValidationMessage("Zipcode", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
The following example shows the Details view:
<h2>Details</h2>
<fieldset>
<legend>Fields</legend>
<p>
Id:
<%= Html.Encode(Model.Id) %>
</p>
<p>
Name:
<%= Html.Encode(Model.Name) %>
</p>
<p>
Age:
<%= Html.Encode(Model.Age) %>
</p>
<p>
Street:
<%= Html.Encode(Model.Street) %>
</p>
<p>
City:
<%= Html.Encode(Model.City) %>
</p>
<p>
State:
<%= Html.Encode(Model.State) %>
</p>
<p>
Zipcode:
<%= Html.Encode(Model.Zipcode) %>
</p>
</fieldset>
<p>
<%=Html.ActionLink("Back to List", "Index") %>
</p>
<h2>Details</h2>
<fieldset>
<legend>Fields</legend>
<p>
Id:
<%= Html.Encode(Model.Id) %>
</p>
<p>
Name:
<%= Html.Encode(Model.Name) %>
</p>
<p>
Age:
<%= Html.Encode(Model.Age) %>
</p>
<p>
Street:
<%= Html.Encode(Model.Street) %>
</p>
<p>
City:
<%= Html.Encode(Model.City) %>
</p>
<p>
State:
<%= Html.Encode(Model.State) %>
</p>
<p>
Zipcode:
<%= Html.Encode(Model.Zipcode) %>
</p>
</fieldset>
<p>
<%=Html.ActionLink("Back to List", "Index") %>
</p>