Data Entry and ASP.NET MVC

(March 2009 Issue)

Dino Esposito

October 30, 2009

10 Min Read
ITPro Today logo

Although still in a beta stage, the ASP.NET MVC Frameworkis definitely being talked about a lot. Indeed, quite a few people arealready using it while contributing to shape its feature set andcharacteristics. The ASP.NET MVC Framework heralds a brand new approach toASP.NET development that is radically opposed to classic Web Forms programming,and, to a large extent, is even incompatible with it. For more information, see "Understanding the ASP.NET MVC Framework" and "Input Validation in ASP.NET MVC."

Picking up Web Forms or the ASP.NET MVC Framework ismostly a matter of preference and attitude, even though quite a few things comedefinitely better and easier with the ASP.NET MVC Framework. The most obviousexample is unit testing. Another aspect that is often mentioned as superior inthe ASP.NET MVC Framework is separation of concerns (SoC). However, while theASP.NET MVC Framework is designed with SoC in mind, adding SoC and a certaindegree of testability to classic ASP.NET is certainly not impossible. The WebClient Software Factory from Microsoft s Patterns and Practices group shows justthat.

However, the ASP.NET MVC Framework also is not perfect.Overall, I d say that it really shines when all you need to do is display data.The structure of the ASP.NET MVC Framework is essentially RESTful, and allowsyou to do a lot of things through URLs. But data display is not all you need todo on a Web site. Data entry is another fundamental block. Admittedly, dataentry with Web Forms is definitely more immediate and maybe more intuitive toset up compared to the MVC Framework. The main reason for this is probably tobe found in the fact that, by design, Web Forms tries to abstract Webprogramming into a Windows-like and RAD programming environment.

In this article, I ll go through an example of form-baseddata entry using the ASP.NET MVC Framework. You ll see that data entry can beequally powerful with ASP.NET MVC, and can result in even more elegant coding,but requires a different model and mindset.

 

Data Entry in ASP.NET

Classic ASP.NET bases its programming model on theassumption that state is maintained across postbacks. This is not true at allat the HTTP protocol level, but it is brilliantly simulated using the featureof viewstate and a bit of work in the page lifecycle. The ASP.NET MVC Frameworksimply uses a different pattern that is not page-based. Does this mean moreproductivity? Or assure less productivity? It actually depends on yourpreferences and attitude.

If you ve grown up with Web Forms and its server controls,you may be shocked when transported into the MVC model. If you re open-minded,or are coming from a non-ASP.NET world, then it will definitely be love atfirst sight. After the first builds of the ASP.NET MVC Framework, many people,quite reasonably, complained about data entry in the new framework. Today,things are going to be significantly different.

Data entry is a scenario where server controls reallyshine, and their postback and viewstate overhead saves you from a lot of work.Server controls also give you a powerful infrastructure for input validation.

In the latest builds of the ASP.NET MVC Framework, you musttake care of the design and configuration of the forms but if you carefullyname input elements, you benefit from a number of facilities that fall underthe umbrella of the Convention over Configuration paradigm (CoC). Originated inRuby on Rails, this paradigm is becoming popular in the .NET space, thanks to theASP.NET MVC Framework. Essentially, CoC means coding by convention in order toreduce the points of decision in a class gaining in simplicity, whilemaintaining a good deal of flexibility. Among other things, conventions in theASP.NET MVC Framework data entry mean that the framework can automatically mapinput data from the HTTP packet down to the model. Let s explore and learn moredetails.

 

Form Posting

The sample application I ll discuss allows users to pick acustomer from a dropdown list, then display an edit form where updates can beentered and saved. The application is based on three actions:

  • list the customers

  • edit a particular customer

  • save changes for a particular customer

The model is built with LINQ to SQL, and consists of thesole Northwind s Customers table. Figure 1 shows the controller s action thatpopulates the dropdown list to offer the first screen to the user.

public ActionResult List(){ // Get the data to populate the list of customers using(NorthwindDataContext context = new NorthwindDataContext()) {   var data = (from c in context.Customers                select new { c.CustomerID, c.CompanyName }).ToList();   ViewData["listCustomers"] = new SelectList(data, "CustomerID", "CompanyName"); } return View("List");}

Figure 1: A Listaction.

 

By clicking a submit button after selecting a customerfrom the list, the user submits a POST request for an Edit action on theInputController class:

<% Html.BeginForm("Edit", "Input"); %><%= Html.DropDownList("listCustomers", ViewData["listCustomers"] as SelectList)%><% Html.EndForm(); %> 

In Figure 2 you can see two implementations of the Editaction, one for a GET request and one for a POST request. The AcceptVerbsattribute specifies the supported verb, whereas the AcceptName attribute breaksthe automatism based on which the name of the method determines the action. Inthis way, you can have multiple implementations for the same action that youuse in different scenarios. Because a dropdown list is used to select thecustomer in the user interface, you can hardly associate beforehand the submitbutton with the correct URL.

[ActionName("Edit"), AcceptVerbs(HttpVerbs.Get)]public ActionResult EditViaGet(string id){ PrepareViewData(id); return View("Edit");}[ActionName("Edit"), AcceptVerbs(HttpVerbs.Post)]public ActionResult EditViaPost(string listCustomers){ // This runs when the user posts from the List page. string customerID = listCustomers; return RedirectToAction("Edit",   new RouteValueDictionary(new { id = customerID }));}

Figure 2: Distinctmethods for GET/POST edit actions.

The URL would be something like /input/edit/id, but youjust don t know at design time which customer ID will actually be selected atrun time. For this reason, you need a GET method that resolves direct queriesfor URLs like /input/edit/id; at the same time, you also need a POST methodthat figures out the customer ID from the Request object. The signature of theEditViaPost method in Figure 2 only shows the sense of CoC. If the parametername matches an element in the Request.Form collection, then that value will bepassed to the method. Needless to say, using Request.Form directly also wouldwork:

string customerID = Request.Form["listCustomers"] as string; 

The EditViaPost method redirects to the GET handler so youcan have a single edit point and a single URL to enter in edit mode (see Figure3).


Figure 3: Editing a customer srecord.

The edit view contains an optional user control used foractual data entry. The user control is displayed only if the ViewData containerincludes data for it:

<%   object data = ViewData["CustomerData"];   if (data == null)       return;   Html.RenderPartial("CustomerEdit", data);%> 

As you can see, the Save button in the user control pointsto /input/update/alfki. This is the result of the following code in theCustomerEdit.ascx user control:

<% Html.BeginForm("Update", "Input", new { id = ViewData.Model.CustomerID }); %> 

Nothing has been done so far that is relevant to dataentry. The core of the data entry stuff is in the user control.

 

Collecting Posted Data

In Figure 4 you can see the structure of the input formthrough which the user can edit the selected customer s record.

    ID   <%= ViewData.Model.CustomerID %>     Company   <%= Html.TextBox("CompanyName", ViewData.Model.CompanyName,         new Dictionary() { { "class", "textBox" } }) %>       Contact     <%= Html.TextBox("ContactName", ViewData.Model.ContactName,          new Dictionary() { { "class", "textBox" } }) %>  

Figure 4: A sampleinput form.

The code-behind class of the user control utilizes astrongly-typed model, as in this code snippet:

public partial class CustomerEdit :   System.Web.Mvc.ViewUserControl{}

Through the properties exposed out of the ViewData.Modelobject, you can then populate the various input fields of the user control. Toadd proper style information, use a dictionary of HTML attributes. To takeadvantage of the CoC support in the ASP.NET MVC Framework, you might want toname text boxes after properties in the Customer type. For example, a TextBoxelement named CompanyName automatically will be associated to the CompanyNameparameter on the Update action within the controller. At display time, thebinding is determined by a hard-coded value:

<%= Html.TextBox("CompanyName", ViewData.Model.CompanyName, new Dictionary() { { "class", "textBox" } }) %>

Once the form is posted, the ASP.NET MVC Framework willuse matching names to associate the value entered in an input field to acorresponding parameter in the action method signature:

public ActionResult Update(string customerID, string companyName, string contactName, ...) 

However, if you don t like such a long signature, you canopt for the following:

public ActionResult Update(string id, FormCollection form)

FormCollection is simply an abstraction over Request.Formthat, among other things, makes testing easier. The form collection contains asmany elements as there are in the low-level ASP.NET Form collection. How canyou update the model with posted data? An obvious answer is the following code:

Customer cust = from c in context.Customers   where c.CustomerID==id ...;cust.CompanyName = Request.Form["txtCompany"]; 

This code works regardless of the name you assign to theinput fields. For testing purposes, you might want to make Request.Formdisappear from the controller s code. If you opted for CoC and a signature thatlists one parameter for each input field, you can do the following:

// Variable companyName is a method's argumentcust.CompanyName = companyName;  

Finally, if you opted for a FormCollection in the method ssignature, here s the code that works for you:

cust.CompanyName = form["CompanyName"];  

In most recent builds, though, Microsoft introduced theUpdateModel method on the Controller class. This method accesses theRequest.Form collection (or any collection you specify) and updates theprovided object. The resulting code for the Update action is significantlysimpler and easier to read:

public ActionResult Update(string id){   Customer cust = from c in context.Customers      where c.CustomerID==id ...;   UpdateModel(cust);   context.SubmitChanges();   :}

One thing is left: How can you update the user interfaceto reflect a successful update?

 

Updating the User Interface

The user interface of Figure 3 contains the details of thecurrent record, as well as the list to pick up a new one. After the update ismade, though, you want to show a message to the user but, at the same time, youdon t want to navigate to another view just to show a message. And you don twant to maintain two distinct views that basically only differ for a label.Subsequently, the obvious solution is having the Update method redirect to theEdit action, as shown here:

return RedirectToAction("Edit", new RouteValueDictionary(new { id = id }));

In this way, you are now displaying the same page as inFigure 3, plus a message that reflects the success or failure of the updateoperation. The edit view will contain a simple tag for themessage; but how are you passing data from the Update method to the Edit view?You can t pass custom data through the RedirectToAction or View methods.However, you can use the TempData container:

public ActionResult Update(string id){   :   TempData["OutputMessage"] = "Successfully updated!";   return RedirectToAction("Edit",     new RouteValueDictionary(new { id = id }));} In the Edit view, you have the following:  <%= TempData["OutputMessage"] %> 

Does this exhaust the data entry topic? Not at all. Errorhandling and validation are two strongly related topics that deserve an articleof their own. Next month I ll pick up from here to introduce the topic ofvalidation in an MVC model and discuss a bit of jQuery to animate messages.

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like