http://www.developer.com/

Back to article

Accepting Input and Manipulating Data in ASP.NET MVC


December 10, 2008

ASP.NET MVC is an interesting new technology that supports separating the user interface from the application logic, and the data storage from the data manipulation. When you create ASP.NET MVC applications, the old ASP.NET style of accepting input from the user is no longer the best option. Furthermore, you will also need to learn how to dynamically accept input from the user.

Assume you want to implement a method to process an URL, such as "/products/edit/NNN", where NNN would be an ID number of a product. As you learned in my earlier article, "ASP.NET MVC 101," you would start by writing a public method named Edit in the ProductsController class of your application.

However, how would you be able to retrieve the product ID that the user wants to edit? In regular ASP.NET, you would access the Request object and find the information you want. But in ASP.NET MVC, you have an surprisingly easy alternative. All you need to do is declare the Edit method so that it accepts a parameter with the name "Id", and you are done!

You might wonder how this works. To find the answer, you need to recall the declaration of the default route you saw earlier. Remember how it contained the ID parameter as the third parameter? If you declare a controller method with this parameter, you will automatically get the value as a parameter to your controller when the user accesses the URL.

The routing system in MVC applications looks for particular controller methods that have parameters that match the route parameters. The default route is "{controller}/{action}/{id}", and given the request URL of "/products/edit/123", the system looks for an Edit method that takes parameter with the name "ID" (case is not important). That is, the naming of parameters is important, but so is also typing.

If you want, you can use strongly typed parameters, like an int for parameters that should have an integer value. However, if the request URL is "/products/edit/ABC", "ABC" cannot be converted to an int, and an error results. The string type is the safest bet because all URLs can be represented as strings.

Note that the request could also be simply "/products/edit", in which case there would be no value for the ID parameter. If you had declared your Edit action method with a parameter "int id", an error would result at runtime because ASP.NET MVC would not know which parameter value to pass. To work around the issue, you could use a Nullable int type, and declare your method like this:

public ActionResult Edit(int? id)

With this declaration, the call would still succeed even if the user called the Edit method without an ID parameter. In the controller code, you then could use the ID parameter's HasValue property to check whether there is indeed a value. If you choose to use a string type for the parameter instead, you would simply check whether the parameter is null just like with every other reference type.

Note that, in theory, you also could overload controller methods to accept different parameters, or not accept parameters at all. However, at least in ASP.NET MVC Beta 1, if you try to do this, you will run into errors like this at runtime:

The current request for action 'Edit' on controller type
   'ProductsController' is ambiguous between the following
   action methods...

Given this, it is usually best to use Nullable types in controller methods, or alternatively use reference types.

Allowing the User to Modify Data

In the previous section, you learned how to access parameters from the URL and how ASP.NET MVC parses these into controller parameter values. If you wanted to implement a product edit function in your application, you would need to continue implementing the Edit method in the ProductsController class that you declared previously.

Assume that you would declare the Edit method like this:

public ActionResult Edit(int? id)

Now, if the user would use an URL having an ID parameter of 123, you then could write code in the controller to retrieve the product data from your database given the product ID. ASP.NET MVC doesn't require you to use any particular database access method, so you could use ADO.NET, LINQ, or even your custom methods.

Now, assume your implementation has retrieved a Product class full of data. You already know how to display this data using the view page's model, but how would you easily create editable fields in the HTML code? In regular ASP.NET applications, you would use server-based controls such as the TextBox, CheckBox, or even a DetailsView. Although you can use these controls in ASP.NET MVC applications, you also can use regular HTML form controls directly; in other words, the traditional INPUT fields.

Assuming you are already familiar with the workings of ASP.NET's server-side controls, you might want to learn how the more traditional HTML controls work in ASP.NET MVC applications. Manually creating HTML code for these controls would not be very productive, so it is best to let the framework do the work.

To get what you want, you can use the previously discussed HtmlHelper class in the view page, and use its control creation methods like TextBox, CheckBox, and ListBox. For example, to enable editing of the product's properties, you could write code in your .aspx file like this:

<% using (Html.BeginForm("update", "products")) { %>
<table>
   <tr>
      <td>ID: </td>
      <td><%= Html.TextBox("Id", ViewData.Model.Id) %></td>
   </tr>
   <tr>
      <td>Name: </td>
      <td><%= Html.TextBox("Name", ViewData.Model.Name) %></td>
   </tr>
   ...
</table>
<% } %>

Just as with server-side ASP.NET controls, you need to have an HTML "form" tag somewhere in your .aspx file. This tag should at a minimum contain a "method" attribute (usually POST) and an "action" that specifies the destination of the form post. The HtmlHelper's method BeginForm does just this. Recall that a "runat=server" attribute is not required for HTML-based form controls.

Note that ASP.NET MVC allows you to process forms using the same actions that you use to edit data, but it is often wiser to have a separate action to process the form. For instance, given an URL like "/products/edit/123", you might want to create an action for "/products/update/123" to handle the form processing.

If you decide to go the route of a separate action handling the form, you actually have two options to get the data back from the form: using a controller method called UpdateFrom, or by using model binders.

With the UpdateFrom method, you pass in an object instance with public properties, and the method then looks at the form fields and sets the object's properties accordingly. This way, all HTML form fields can be processed easily. Optionally, you can pass in an array such as Request.Form.AllKeys to specify which properties you want to update. For instance:

public ActionResult Update()
{
   Models.Product product = new Models.Product();
   UpdateModel(product, Request.Form.AllKeys);
   return View();
}

Although the UpdateFrom method works, it has its problems. For example, properties with types other than strings can cause trouble, and the programming pattern looks more or less like a black box. The cleaner approach is to let the MVC framework bind the model back to a strongly typed parameter value in the action method.

To do this, you would simply declare the action method processing the form like this:

public ActionResult Update(Models.Product product)

Given this declaration, the MVC framework knows that you expect a strongly-typed model. With a method declaration like this in place, the framework automatically looks for HTML form fields with the name "parametername.property"; for instance, "product.Id" and "product.Name". This would mean that you would have to edit your HTML form generation code slightly:

<%= Html.TextBox("product.Id", ViewData.Model.Id) %>
<%= Html.TextBox("product.Name", ViewData.Model.Name)%>

Note that the HTML field names are not case sensitive, so either "ID" or "Id" will do. Also, you can test at runtime if the controller's Model.IsValid property is true to see whether the data entered by the user conformed to the model object's property types. For instance, if an invalid numeric string is entered, Model.IsValid will be false.

Conclusion

ASP.NET MVC provides a clean separation among the user interface, application logic, and data presentation. To use this new technology efficiently, you need to learn how to do things the MVC way.

In this article, you learned how ASP.NET MVC applications map URL parts into controller method parameters, and how you can process those parameter values. Furthermore, you also saw how to manipulate database data, and effectively accept input from the user.

The new Beta 1 release of ASP.NET MVC also contains an explicit "go-live" license; this means that, if you want, you can put your ASP.NET MVC web application into production today. Microsoft has also engineered MVC Beta 1 so that it is easy to deploy: by copying the MVC assemblies (DLLs) into your web application's Bin subfolder, you can run ASP.NET MVC application on web servers that do not have MVC assemblies preinstalled (see Figure 1).



Click here for a larger image.

Figure 1: Deploying to a production environment is well documented from the ground up.

Good luck with this new .NET web application model!

Jani Järvinen

Links

The following links help you get started with ASP.NET MVC.

About the Author

Jani Järvinen is a software development trainer and consultant in Finland. He is a Microsoft C# MVP, a frequent author, and has published three books about software development. He is the group leader of a Finnish software development expert group named ITpro.fi. His blog can be found at http://www.saunalahti.fi/janij/. You can send him mail by clicking on his name at the top of the article.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date