top button
Flag Notify
    Connect to us
      Facebook Login
      Site Registration

Facebook Login
Site Registration

Validating Multiple Data Model Properties

+5 votes
75 views

In my previous article I wrote about displaying model state errors inside databound controls such as GridView. In that example we used data annotation validators to perform the validations. While data annotation validators do their job quite well they are inherently applied to only one data model property at a time. For example, the [StringLength] or [Required] attributes validate only one property under consideration. However, sometimes your validation rule involves multiple data model properties. In such cases data annotation validators won't be of much use. Luckily there are other validation techniques that can come to your rescue. This article is doing to discuss two of them:

  • Validating using ModelState dictionary
  • Validating using IValidatableObject interface

In the first technique you use ModelState dictionary to access the model properties, check their values and then flag errors if necessary. Let's understand how this technique is used with an example. The example mentioned here uses the same Employee data model class that was used in my previous article. So, if you haven't created the Entity Framework data model yet take a moment to generate it from Northwind database by referring this article.

Now, consider the following code that shows the GridView_UpdateItem method:

public void GridView1_UpdateItem(int employeeid)
{
  NorthwindEntities db = new NorthwindEntities();
  Employee item = db.Employees.Find(employeeid);
  if (item == null)
  {
    ModelState.AddModelError("", String.Format("Item with id {0} was not found", employeeid));
  }
  else
  {
    TryUpdateModel(item);
    if (item.FirstName.Length > 10 && item.LastName.Length > 10)
    {
      ModelState.AddModelError("", "First Name and Last Name are invalid!");
    }
    if (ModelState.IsValid)
    {
      db.SaveChanges();
    }
  }
}

Notice the code shown in bold letters. The code checks the length of FirstName and LastName model properties. Accordingly an error message is added in the ModelState dictionary using AddModelError() method. Supplying the first parameter as an empty string indicates that this is a model level error rather than a property level error. If you want to associate the error under consideration to a specific model property you can specify the name of the property as the first parameter of the AddModelError() method. The second parameter of the AddModelError() method specifies the error message that will be displayed in the ValidationSummary control.

Ok. Now let's see the other technique - IValidatableObject interface. In this technique you need to implement IValidatableObject interface on the data model class. The IValidatableObject interface contains Validate() method that should return a collection of ValidationResult objects. Each ValidationResult represents an error condition with the underlying data model. Since Employee model class is an EF designer generated class the best place to implement the IValidatableObject interface is the Employee partial class you created for the sake of associating metadata class with the model class. The following code shows how the Validate() method can be implemented on the data model.

[MetadataType(typeof(EmployeeMetadata))]
public partial class Employee:IValidatableObject
{
  public IEnumerable Validate(ValidationContext validationContext)
  {
    List errors = new List();
    if (FirstName.Length>10 && LastName.Length>10)
    {
      ValidationResult err = new ValidationResult("Invalid First Name and Last Name");
      errors.Add(err);
    }
    return errors;
  }
}

Notice the code shown in bold letters. The Validate() method receives ValidationContext as a parameter and returns IEnumerable of ValidationResult objects. Inside, it creates a generic List of ValidationResult objects. It then checks for the same validation rule as before and if the validation rule is being violated adds a new ValidationResult to the generic list. The ValidationResult constructor accepts an error message that flags the error. Finally the generic List is returned from the Validate() method.

Which of the two techniques you use depends on your requirement. The ModelState technique puts the validation code in the web form since ModelState is a property of the Page base class. That means your validation logic gets coupled with the rest of the web form code. This may be alright if the validation rule is to be checked for that form alone. However, if the validation rule is applicable inside all the web forms (or wherever Employee data model class is being used) then IValidatableObject technique would be more useful. Using IValidatableObject technique allows you to put the validation rule under consideration in the model class itself. So, depending on your requirement you should pick the appropriate technique.

While the above example illustrates both of these techniques for a web forms application they can be used with ASP.NET MVC application also in the same fashion. If you are using ASP.NET MVC the ModelState dictionary technique will go inside an action method whereas the IValidatableObject technique remains unchanged.

That's it for now! Keep coding.

posted Oct 27, 2016 by Shivaranjini

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button


Related Articles

ASP.NET model binding is quite powerful and flexible. It caters to most of the scenarios without much configuration from developers. However, at times you may need to intervene in order to achieve the desired model binding effect. One such situation is when you use multiple instance of a partial view on a view. This article shows one possible approach to deal with such situations.

Suppose that you have a web page as shown below:

image

As shown in the above figure the web page captures OrderID, CustomerID, ShippingAddress and BillingAddress from the end user. This information is stored in a model class - Order - that looks like this:

public class Order
{
    public int OrderID { get; set; }
    public int CustomerID { get; set; }

    public Address ShippingAddress { get; set; }
    public Address BillingAddress { get; set; }
}

public class Address
{
    public string Street1{get;set;}
    public string Street2{get;set;}
    public string Country{get;set;}
    public string PostalCode{get;set;}
}

The Order class consists of four public properties namely OrderID, CustomerID, ShippingAddress and BillingAddress. Notice that OrderID and CustomerID are integer properties whereas ShippingAddress and BillingAddress properties are of type Address. The Address class is also shown and consists of four string properties - Street1, Street2, Country and PostalCode.

Now let's assume that the whole page is rendered using two ASP.NET MVC Partial Pages. The OrderID and CustomerID is captured using _BasicDetails.cshtml as shown below:

@model Demo.Models.Order

<table>
    <tr>
        <td>@Html.LabelFor(m=>m.OrderID)</td>
        <td>@Html.TextBoxFor(m=>m.OrderID)</td>
    </tr>
    <tr>
        <td>@Html.LabelFor(m=>m.CustomerID)</td>
        <td>@Html.TextBoxFor(m=>m.CustomerID)</td>
    </tr>
</table>

Note that _BasicDetails partial page has its model set to Order class. The partial page then uses LabelFor() and TextBoxFor() helpers to display a label and textbox for the OrderID and CustomerID model properties respectively.

The address information is captured using _Address.cshtml as shown below:

@model Demo.Models.Address

<table>
    <tr>
        <td>@Html.LabelFor(m=>m.Street1)</td>
        <td>@Html.TextBoxFor(m=>m.Street1)</td>
    </tr>
    <tr>
        <td>@Html.LabelFor(m=>m.Street2)</td>
        <td>@Html.TextBoxFor(m=>m.Street2)</td>
    </tr>
    <tr>
        <td>@Html.LabelFor(m=>m.Country)</td>
        <td>@Html.TextBoxFor(m=>m.Country)</td>
    </tr>
    <tr>
        <td>@Html.LabelFor(m=>m.PostalCode)</td>
        <td>@Html.TextBoxFor(m=>m.PostalCode)</td>
    </tr>
</table>

The _Address partial page has Address class as its model and uses LabelFor() and TextBoxFor() helpers to display model properties.

The Index view that makes use of _BasicDetails and _Address partial pages to form the complete page is shown below:

@model Demo.Models.Order

...
@using(Html.BeginForm("ProcessForm","Home",FormMethod.Post))
{
  <h3>Basic Details</h3>
  @Html.Partial("_BasicDetails")

  <h3>Shipping Address</h3>
  @Html.Partial("_Address",Model.ShippingAddress)
        
  <h3>Billing Address</h3>
  @Html.Partial("_Address",Model.BillingAddress)
        
  <input type="submit" value="Submit" />
}
</body>
</html>

The Index view renders the _BasicDetails partial page using Partial() helper. Since the model for Index view is Order class, the same is available to the _BasicDetails partial page. Then two instances of _Address partial page are rendered on the page to capture ShippingAddress and BillingAddress respectively. Recollect that _Address has Address class as its model. So, Model.ShippingAddress and Model.BillingAddress are passed to the Partial() helper.

The above form submits to ProcessForm action method that looks like this:

public ActionResult ProcessForm(Order ord)
{
    //do something with Order object here
    return View("Index");
}

And the Index() action method looks like this:

public ActionResult Index()
{
    Order ord = new Order();
    ord.BillingAddress = new Address();
    ord.ShippingAddress = new Address();
    return View(ord);
}

Both of these methods are quite straightforward and need no explanation.

Now comes the important and tricky part. If you run the application at this stage, you will get the following HTML markup in the browser (unwanted markup has been removed for the sake of clarity):

<form action="/Home/ProcessForm" method="post">        
<h3>Basic Details</h3>
<table>
    <tr>
        <td><label for="OrderID">OrderID</label></td>
        <td><input id="OrderID" name="OrderID" type="text" /></td>
    </tr>
    <tr>
        <td><label for="CustomerID">CustomerID</label></td>
        <td><input id="CustomerID" name="CustomerID" type="text" /></td>
    </tr>
</table>
<h3>Shipping Address</h3>
<table>
    <tr>
        <td><label for="Street1">Street1</label></td>
        <td><input id="Street1" name="Street1" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="Street2">Street2</label></td>
        <td><input id="Street2" name="Street2" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="Country">Country</label></td>
        <td><input id="Country" name="Country" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="PostalCode">PostalCode</label></td>
        <td><input id="PostalCode" name="PostalCode" type="text" value="" /></td>
    </tr>
</table>
<h3>Billing Address</h3>
<table>
    <tr>
        <td><label for="Street1">Street1</label></td>
        <td><input id="Street1" name="Street1" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="Street2">Street2</label></td>
        <td><input id="Street2" name="Street2" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="Country">Country</label></td>
        <td><input id="Country" name="Country" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="PostalCode">PostalCode</label></td>
        <td><input id="PostalCode" name="PostalCode" type="text" value="" /></td>
    </tr>
</table>
<input type="submit" value="Submit" />
</form>

Notice the markup in bold letters. Can you see HTML elements with duplicate id and name attributes? That's because you are rendering two instance of the _Address partial page. The model binding framework requires that the HTML fields follow this naming convention for the model binding to work as expected:

<input id="ShippingAddress_Street1" 
       name="ShippingAddress.Street1" type="text" value="" />
<input id="BillingAddress_Street1" 
       name="BillingAddress.Street1" type="text" value="" />

As you can see from the above markup the id and name attributes must fully quality the model property being bound. In the absence of such a naming pattern the Order instance won't be bound as expected as confirmed by the following figure:

image

As shown above the ShippingAddress and BillingAddress properties are null whereas OrderID and CustomerID are captured successfully.

The above problem can be solved by using a variation of the Partial() helper while rendering the _Address partial page. The following code shows how this is done:

<h3>Basic Details</h3>
@Html.Partial("_BasicDetails")

<h3>Shipping Address</h3>
@Html.Partial("_Address", 
  new ViewDataDictionary() 
  { 
    TemplateInfo = new TemplateInfo() 
      { HtmlFieldPrefix = "ShippingAddress" } })

<h3>Billing Address</h3>
@Html.Partial("_Address", 
  new ViewDataDictionary() 
    { TemplateInfo = new TemplateInfo() 
      { HtmlFieldPrefix = "BillingAddress" } })

The variation of Partial() helper used above uses ViewDataDictionary parameter to specify TemplateInfo. The HtmlFieldPrefix property of the TemplateInfo is set to ShippingAddress for the first instance and to the BillingAddress for the second instance.

If you run the application now, you will find the following markup in the browser:

<form action="/Home/ProcessForm" method="post">
<h3>Basic Details</h3>
<table>
    <tr>
        <td><label for="OrderID">OrderID</label></td>
        <td><input id="OrderID" name="OrderID" type="text" value="0" /></td>
    </tr>
    <tr>
        <td><label for="CustomerID">CustomerID</label></td>
        <td><input id="CustomerID" name="CustomerID" type="text" value="0" /></td>
    </tr>
</table>
<h3>Shipping Address</h3>
<table>
    <tr>
        <td><label for="ShippingAddress_Street1">Street1</label></td>
        <td><input id="ShippingAddress_Street1" 
                   name="ShippingAddress.Street1" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="ShippingAddress_Street2">Street2</label></td>
        <td><input id="ShippingAddress_Street2" 
                   name="ShippingAddress.Street2" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="ShippingAddress_Country">Country</label></td>
        <td><input id="ShippingAddress_Country" 
                   name="ShippingAddress.Country" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="ShippingAddress_PostalCode">PostalCode</label></td>
        <td><input id="ShippingAddress_PostalCode" 
                   name="ShippingAddress.PostalCode" type="text" value="" /></td>
    </tr>
</table>
<h3>Billing Address</h3>
<table>
    <tr>
        <td><label for="BillingAddress_Street1">Street1</label></td>
        <td><input id="BillingAddress_Street1" 
                   name="BillingAddress.Street1" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="BillingAddress_Street2">Street2</label></td>
        <td><input id="BillingAddress_Street2" 
                   name="BillingAddress.Street2" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="BillingAddress_Country">Country</label></td>
        <td><input id="BillingAddress_Country" 
                   name="BillingAddress.Country" type="text" value="" /></td>
    </tr>
    <tr>
        <td><label for="BillingAddress_PostalCode">PostalCode</label></td>
        <td><input id="BillingAddress_PostalCode" 
                   name="BillingAddress.PostalCode" type="text" value="" /></td>
    </tr>
</table>
<input type="submit" value="Submit" />
</form>

As expected the id and name attributes are now fully qualified and hence the model binding will happen as expected as shown below:

image

The model binding now correctly captures ShippingAddress as well as BillingAddress information.

READ MORE

Web API allows you to create RESTful services easily. A typical Web API implements GET, POST, PUT and DELETE verbs by writing methods intended to handle each of these HTTP verbs. Although the actual method implementation can be anything as per your application requirement, you need to follow certain method signatures. For example, consider the following methods:

public void Post(MyClass data)
{
  //do something with emp and cust here.
}

public void Put(int id, MyClass data)
{
  //do something here.
}

The above code shows the template for Post() and Put() methods. The Post() method signature accepts the data accompanying the request as MyClass object. You can't have multiple parameters to Post() method. On the same lines the Put() method accepts the request data as the second parameter. Here again you can't have more parameters to receive the data. In short, a request to Web API wraps the data accompanying the request in one single object.

At times, however, you need to pass multiple independent pieces of information to the Web API. This poses a limitation to developers. You can overcome this problem in two ways:

  • Accept data as array or ArrayList of objects.
  • Create a class that wraps all the individual pieces and receive data as an instance of that class.

Let's see how these approaches look like at code level. Suppose you have two classes - Employee and Customer - as shown below:

public class Employee
{
  public int EmployeeID { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

public class Customer
{
  public string CustomerID { get; set; }
  public string CompanyName { get; set; }
}

The Employee class has three properties namely EmployeeID, FirstName and LastName. The Customer class has two properties namely CustomerID and CompanyName. Now assume that you will be calling a Web API to store an Employee and a Customer into the database. In this example, let's assume you will be using jQuery to call the Web API (the approach remains the same even with HttpClient).

Next, consider the following Web API method:

public void Post(object[] data)
{
 Employee emp = JsonConvert.DeserializeObject<Employee>(data[0].ToString());
 Customer cust = JsonConvert.DeserializeObject<Customer>(data[1].ToString());

 //do something with emp and cust here.
}

As you can see the Post() method has one parameter of type object array. If you want you can also use ArrayList instead of plain array. Inside, the code uses Json.NET to deserialize individual objects into their respective types. Of course, in this case you must ensure that the object array contains the objects in a specific sequence. In this example, Employee object must be available at index 0 and Customer object at index 1. Once you get the Employee and Customer objects you can store them into a database or process them any way you want.

The client side jQuery code that calls this Web API method is as follows:

$(document).ready(function () {
  var objEmployee = {};
  objEmployee.EmployeeID = 1;
  objEmployee.FirstName = "Nancy";
  objEmployee.LastName = "Davolio";

  var objCustomer = {};
  objCustomer.CustomerID = "ALFKI";
  objCustomer.CompanyName = "Alfreds Futterkiste";

  var strFinal = "[" + JSON.stringify(objEmployee) + "," + 
                       JSON.stringify(objCustomer) + "]";

  var options = {};
  options.url = "/api/Values";
  options.type = "POST";
  options.data = strFinal;
  options.dataType = "json";
  options.contentType = "application/json";
  options.success = function () { alert("Success"); };
  options.error = function () { alert("Error"); };

  $.ajax(options);
});

 The above jQuery code creates a JavaScript object that mimics the Employee class on the server. It sets the EmployeeID, FirstName and LastName properties of the object to 1, Nancy and Davolio respectively. On the same lines another JavaScript object is created that mimics the Customer object on the server. The CustomerID and CompanyName properties of this object are set to ALFKI and Alfreds Futterkiste respectively. Then these two JavaScript objects are combined to form an array in JSON format. The strFinal variable holds this array. A sample array looks like this:

[
  {
    "EmployeeID" : 1,
    "FirstName"  : "Nancy",
    "LastName"   : "Davolio"
  }
  ,
  {
    "CustomerID" : "ALFKI",
    "CompanyName"  : "Alfreds Futterkiste"
  }
]

Then an Ajax call is made to the Web API. The options object provides all the settings required to make the call. The type property of the options object points to the Web API (/api/Values) in this case. The type property indicates the type of request (POST in this case). The data property is set to the strFinal variable. The dataType and contentType properties are set to json and application/json respectively. The success and error properties simply point to functions that display a message using an alert dialog. Finally, jQuery $.ajax() is used to invoke the Web API.

To test this code, run the application after setting a breakpoint in the Post() method. The following figure shows how the data is received in the object array:

image

As you can see the data array has two elements. Each element is of type string. The string represents the JSON representation of the respective object.

In the second approach you can wrap the Employee and Customer objects into another class, say EmployeeCustomer, as shown below:

public class EmployeeCustomer
{
  public Employee EmployeeData { get; set; }
  public Customer CustomerData { get; set; }
}

The EmployeeCustomer class has two properties - EmployeeData and CustomerData - that are of type Employee and Customer respectively. The Post() method signature is like this:

public void Post(EmployeeCustomer data)
{
  Employee emp = data.EmployeeData;
  Customer cust = data.CustomerData;
  //do something with emp and cust here.
}

In this case Post() accepts a parameter of type EmployeeCustomer. To call this Web API method from the client side you can use the following jQuery code:

$(document).ready(function () {
  var objEmployee = {};
  objEmployee.EmployeeID = 1;
  objEmployee.FirstName = "Nancy";
  objEmployee.LastName = "Davolio";

  var objCustomer = {};
  objCustomer.CustomerID = "ALFKI";
  objCustomer.CompanyName = "Alfreds Futterkiste";

  var strFinal = JSON.stringify({ 
                 EmployeeData: objEmployee, 
                 CustomerData: objCustomer });

  var options = {};
  options.url = "/api/Values";
  options.type = "POST";
  options.data = strFinal;
  options.dataType = "json";
  options.contentType = "application/json";
  options.success = function () { alert("Success"); };
  options.error = function () { alert("Success"); };

  $.ajax(options);
});

Most of the code shown above is identical to the previous example except the line marked in bold letters. This time you create a JavaScript object with two properties - EmployeeData and CustomerData. You set these properties to the objEmployee and objCustomer respectively. The strFinal variable contains the JSON representation of this JavaScript object.

When you run the above code after setting a breakpoint at the Post() method you will see the EmployeeCustomer object as follows:

image

As you can see the EmployeeData and CustomerData properties of the EmployeeCustomer object are populated as expected.

READ MORE

In this section, you will learn about the Model in ASP.NET MVC framework.

Model represents domain specific data and business logic in MVC architecture. It maintains the data of the application. Model objects retrieve and store model state in the persistance store like a database.

Model class holds data in public properties. All the Model classes reside in the Model folder in MVC folder structure.

Let's see how to add model class in ASP.NET MVC.

Adding Model:

Open our first MVC project created in previous step in the Visual Studio. Right click on Model folder -> Add -> click on Class..

In the Add New Item dialog box, enter class name 'Student' and click Add.

Create Model Class

Create Model Class

This will add new Student class in model folder. Now, add Id, Name, Age properties as shown below.

Example: Model class

namespace MVC_BasicTutorials.Models
{
    public class Student
    {
        public int StudentId { get; set; }
        public string StudentName { get; set;  }
        public int Age { get; set;  }
    }
}


So in this way, you can create a model class which you can use in View. You will learn how to implement validations using model later.

Learn how to create a View in the next section.

READ MORE

ASP.NET model binding framework takes care of validating a model based on data annotation validations. This works well when a model is being bound with request data. However, at times you may need to create and fill a model programmatically. In such cases, although the model properties are decorated with data annotation validators, they won't validate the data because they are not invoked at all. Consider a situation wherein users are uploading CSV files from the client machine on to the server. Your application is supposed to read those files and assign the values from the file to the model properties. The model objects are then stored to some database. In this case data validations won't fire and your model will be held in an invalid state. Since model validations are not being invoked ModelState.IsValid will always return true and there won't be any easy way to detect and display these validation failures. Luckily, ASP.NET MVC allows you to validate a model object via code. This article shows how.

Let's assume that you have a UserInfo model class as shown below:

public class UserInfo
{
    [Required]
    [StringLength(100, MinimumLength = 10)]
    public string FirstName { get; set; }

    [Required]
    [StringLength(100, MinimumLength = 10)]
    public string LastName { get; set; }

    [Required]
    public DateTime BirthDate { get; set; }
}

The UserInfo class consists of three properties - FirstName, LastName and BirthDate. All the model properties are decorated with [Required] attribute. Additionally, FirstName and LastName properties are decorated with [StringLength] attribute. Both of these properties also have MinimumLength set to 10 just for the sake of testing.

Now consider a view (Index.cshtml) as shown below:

image

The Index view shown above consists of a file upload control and a button. You are supposed to select a CSV file that contains data suitable for UserInfo model. Upon selecting a CSV file and clicking the Upload button success or error message will be displayed in another view (UploadResult.cshtml). A sample CSV file would look like this:

Nancy,Davolio,1/2/1960

The HTML markup that makes Index.cshtml is shown below:

<h1>Select file to be uploaded:</h1>
<form action="/home/savedata" method="post" 
enctype="multipart/form-data">
    <input type="file" name="file1" />
    <input type="submit" value="Upload" />
</form>

The <form> posts the file to SaveData() action method. The SaveData() action does the job of reading the uploaded file, instantiating a model object, setting model properties and then displaying UploadResult view with a success or error message. For example, the following figure shows UploadResult view when the UserInfo model is in invalid state:

image

Notice that the UploadResult view displays validation errors through ValidationSummary() helper.

Now let's see the most important part of the application - SaveData() action method.

public ActionResult SaveData()
{
  if(Request.Files.Count>0)
  {
    HttpFileCollectionBase files = Request.Files;
    foreach(string key in Request.Files)
    {
     HttpPostedFileBase item = Request.Files[key];
     byte[] data = new byte[item.InputStream.Length];
     int byteCount = item.InputStream.Read(data, 0, data.Length);
     string strData = ASCIIEncoding.UTF8.GetString(data).Trim();
     string[] values = strData.Split(',');

     UserInfo info = new UserInfo();
     info.FirstName = values[0];
     info.LastName = values[1];
     info.BirthDate = DateTime.Parse(values[2]);

     if(TryValidateModel(info))
     {
       //save in database here
       ViewBag.Message = "User information is 
                     validated successfully!";
     }
     else
     {
       ViewBag.Message = "Error while validating user info. 
           Please review the errors below 
           and upload the file again!";
     }
     return View("UploadResult", info);
   }
 }
 return View("Index");
}

The SaveData() action iterates through the Request.Files collection and gets hold of the file being uploaded. In our example it is assumed that only one will be uploaded. You don't need to save this file on the server because your interest is in the CSV data in the file. That's why the code reads the content of InputStream of the file using Read() method. The Read() method reads the content in a byte array. This byte array is converted into a string using GetString() method of the ASCIIEncoding class. The string data is further split into a string array using Split() method.

Notice the code marked in bold letters. The code creates a UserInfo object and assigns FirstName, LastName and BirthDate properties from the string array created earlier. If you see the sample data shown earlier you will realize that FirstName and LastName doesn't meet the requirement of [StringLength] attribute. However, at this stage ModelState.IsValid will be true because data validations are not invoked yet.

image

Next, the code uses TryValidateModel() method that invokes the data validations. There are two variations of this method - ValidateModel() and TryValidateModel(). Both of them come from the Controller base class. The former method throws an exception if there are any validation errors whereas the later method silently returns false in case validations fail. If you check ModelState.IsValid after the call to TryValidateModel() you will see that it returns false because there are validation errors.

image

The SaveData() action sends UserInfo model object to the UploadResult view so that validation errors can be displayed. The UploadResult view contains this HTML markup:

@model StProcInCodeFirstDemo.Models.UserInfo
...
<body>
    @ViewBag.Message
    <br /><br />
    @Html.ValidationSummary()
    <br /><br />
    @Html.ActionLink("Upload again!","Index")
</body>
...

Notice that UploadResult view uses ValidateSummary() helper to display the validation error messages. As you might be aware ValidationSummary() or ValidationMessage() helpers work only if ModelState dictionary contains some entries. In our example these entries are added by TryValidateModel() method. Since ModelState dictionary contains some entries the ValidationSummary() iterates through them and displays the validation errors.

That's it! Try running the application a couple of times with invalid as well as valid data and see if everything works as expected.

READ MORE

Showing a single record for editing is quite common and the default model binding of ASP.NET MVC takes care of mapping the form fields to the model properties. However, sometimes you need to edit multiple records. For example, you may want to display an editable grid to the end user filled with existing data. The user can edit the values from multiple rows and hit Save in an attempt to save the data. In this case multiple model objects are being submitted to the action method. The single record editing works on the assumption that form field names from the view match the corresponding model property names. However, when multiple model objects are submitted this assumption is no longer valid. Luckily, by tweaking the form field names you can get this to work as expected. Let's see how.

Begin by creating a new ASP.NET MVC Application. Then right click on the Models folder and add an ADO.NET entity framework data model to it. Configure the model to use Customers table of the Northwind database. The following figure shows this model:

image

Then add HomeController in the Controllers folder. Modify the default Index() action method as shown below:

public ActionResult Index()
{
  NorthwindEntities db=new NorthwindEntities();
  var query = from c in db.Customers
              where c.Country=="UK"
              orderby c.CustomerID
              select c;
  return View(query.ToList());
}

The Index() action method simply selects all the customers from UK and passes them to the Index view as a List of Customer entities.

Then right click on the Index() action method and add Index view. The Index view is where you need to follow certain naming convention to get the desired results:

@model List<ModelBindingToListDemo.Models.Customer>
...
    <h1>List of Customers</h1>
    @using (Html.BeginForm("Index", "Home", FormMethod.Post))
    {
        <table border="1" cellpadding="6">
            @for (int i = 0; i < Model.Count;i++ )
            { 
                <tr>
                    <td>@Html.TextBox("customers[" + @i + "].CustomerID", 
                                      Model[i].CustomerID, 
                                      new { @readonly = "readonly" })</td>
                    <td>@Html.TextBox("customers[" + @i + "].CompanyName", 
                                      Model[i].CompanyName)</td>
                    <td>@Html.TextBox("customers[" + @i + "].ContactName", 
                                      Model[i].ContactName)</td>
                    <td>@Html.TextBox("customers[" + @i + "].Country", 
                                      Model[i].Country)</td>
                </tr>
            }
            <tr>
                <td colspan="4">
                    <input type="submit" value="Submit" />
                </td>
            </tr>
        </table>
    }
...

Notice the markup shown in the bold letters. The code is basically generating names for the textboxes matching the following convention:

customers[n].<Model_Property_Name>

Where n is an index starting from 0 and Model_Property_Name is the name of the properties such as CustomerID, CompanyName, ContactName and Country. For the sake of simplicity the above code uses only four properties form the Customer model class. So, all the textboxes having same index are considered as "one record". This naming convention is required to successfully bind data to the model as you will see later.

The following figures shows how the view looks like in the browser:

image

To see how the textbox names are being generated view the HTML source in the browser.

The above <form> submits the data to Index() method using post method. To handle this data write the second version of Index() action method as follows:

[HttpPost]
public ActionResult Index(List<Customer> customers)
{
  NorthwindEntities db=new NorthwindEntities();
  foreach (Customer cust in customers)
  {
    Customer existing = db.Customers.Find(cust.CustomerID);
    existing.CompanyName = cust.CompanyName;
    existing.ContactName = cust.ContactName;
    existing.Country = cust.Country;
  }
  db.SaveChanges();
  return View();
}

The overloaded Index() method takes a parameter - List of Customer entities. Recollect that this parameter name - customers - is what you used in the view markup earlier. Due the naming conventions followed the model binding framework of ASP.NET MVC transforms the form field values into a generic List of Customer objects. Once received you simply iterate through the List and modify the existing Customer with the new one. You can also put some logic to detect whether a record was really changed or not. Once all the rows are modified SaveChanges() is called to save the changes.

As mentioned earlier the naming convention requires that the index start at 0 and then sequentially increment for each record. If you try changing the start index to say 10, the model binding will fail to bind the data. What if you don't want to start the index from 0? For example, imagine a case where you are removing some row using client side script. In such cases the there might be "gaps" in between various index values. To overcome this situation you can follow an alternate naming convention:

@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
  <table border="1" cellpadding="6">
  @for (int i = 0; i < Model.Count;i++ )
  { 
    <tr>
      <td>
      @Html.Hidden("customers.Index", (@i + 10))
      @Html.TextBox("customers[" + (@i + 10) + "].CustomerID", 
                    Model[i].CustomerID, new { @readonly = "readonly" })
      </td>
      <td>@Html.TextBox("customers[" + (@i + 10) + "].CompanyName", 
                        Model[i].CompanyName)</td>
      <td>@Html.TextBox("customers[" + (@i + 10) + "].ContactName", 
                        Model[i].ContactName)</td>
      <td>@Html.TextBox("customers[" + (@i + 10) + "].Country", 
                        Model[i].Country)</td>
    </tr>
  }
<tr>
...
}

Notice the above markup carefully. Each table row now has a hidden form field. The name of the hidden form field is customers.Index and its value is set to some arbitrary index (i + 10 in this case). Then all the textboxes are assigned names of the form:

 customers[<arbitrary_index>].<model_property_name>

In this case all the textboxes having same index as specified by the hidden field are considered as "one record". In this case the index need not be a number. It can be a string also. Again, recollect that "customers" in the above markup is the name of the parameter of the Index() method.

That's it! Run the application and test if it works as expected.

READ MORE
Contact Us
+91 9880187415
sales@queryhome.net
support@queryhome.net
#280, 3rd floor, 5th Main
6th Sector, HSR Layout
Bangalore-560102
Karnataka INDIA.
QUERY HOME
...