Exercise 1: Model Binding in ASP.NET Web Forms

The new version of ASP.NET Web Forms introduces a number of enhancements focused on improving the experience when working with data. Throughout this exercise, you will learn about strongly typed data-controls and model binding.

Task 1 – Using Strongly-Typed Data-Bindings

In this task, you will discover the new strongly-typed bindings available in ASP.NET 4.5.

  1. Open Visual Studio 11 and open the WebFormsLab-Ex1-Begin.sln solution located in the Source\Ex1-ModelBinding\Begin folder of this lab.
  2. Open the Customers.aspx page. Place an unnumbered list in the main control and include a repeater control inside for listing each customer. Set the repeater name to customersRepeater as shown in the following code.

    In previous versions of Web Forms, when using data-binding to emit the value of a member on an object you’re data-binding to, you would use a data-binding expression, along with a call to the Eval method, passing in the name of the member as a string.

    At runtime, these calls to Eval will use reflection against the currently bound object to read the value of the member with the given name, and display the result in the HTML. This approach makes it very easy to data-bind against arbitrary, unshaped data.

    Unfortunately, you lose many of the great development-time experience features in Visual Studio, including IntelliSense for member names, support for navigation (like Go To Definition), and compile-time checking.

    (Code Snippet – Web Forms Lab - Ex01 – Customers Repeater)

    HTML

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    FakePre-c74e348fd77444d5b61e60ab071a04f0-4dfdabff49574447858ff2d0a73c02e2 <ul> <asp:Repeater ID="customersRepeater" runat="server"> <ItemTemplate> <li> <%# Eval("FirstName") %> <%# Eval("LastName") %> </li> </ItemTemplate> </asp:Repeater> </ul>FakePre-28f9c153e4bf4e71a0d2723a03badd1f-24be520afc65463db1e4c193a2cfb477FakePre-edd85d4333d74080a6f085fd1baff51e-298b140f40d94dd88265cf7eebc59b71

  3. Open the Customers.aspx.cs file.
  4. In the Page_Load method, add code to populate the repeater with the list of customers.

    (Code Snippet – Web Forms Lab - Ex01 – Bind Customers Data Source)

    C#

    protected void Page_Load(object sender, EventArgs e)
    FakePre-18125b460402455ba69a17931fe06262-dd6dca8671034f878dbea855408accdc using (var db = new WebFormsLab.Model.ProductsContext()) { this.customersRepeater.DataSource = db.Customers.ToList(); this.customersRepeater.DataBind(); }FakePre-511a5200bbbb468084df14e2fe8c7792-83d3951dd3e1482caddb54d1f6276d51

    The solution uses EntityFramework together with CodeFirst to create and access the database. In the following code, the customersRepeater is bound to a materialized query that returns all the customers from the database.

  5. Press F5 to run the solution and go to the Customers page to see the repeater in action. As the solution is using CodeFirst, the database will be created and populated in your local SQL Express instance when running the application.

    Figure 1

    Listing the customers with a repeater

    Note:
    In Visual Studio 11, IIS Express is the default Web development server.

  6. Close the browser and go back to Visual Studio.
  7. Open the Customers.aspx page and use the new ItemType attribute in the repeater to set the Customer type as the binding type.

    HTML

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    FakePre-85d46775298040c2910a04975a8feb12-6eb6cf6833b143549c95860a9a2b21daFakePre-00ff22a9eead4eea8b6a95047480b129-2150665428b249d497ed88fea1cc5289 ItemType="WebFormsLab.Model.Customer" FakePre-1d0e8258792544d49714915e2a35052a-f9e8374d400f4bdbb733aecc8ada8cebFakePre-b55c56f3dfb54487bb3146473dbabc8d-0110303aabfb4d8c94abf619c96cdab1FakePre-e5eccaf4a8234ce491c2872b3c2fb320-d17ee930c21b4a3a9ef3bc25d27e31caFakePre-dddd200712804d54a1fa1030df12572a-b7bb1329acc948ee941c998c3fbe9789FakePre-b051bdd342524ff281abc57e0e0c4968-179352a128594f138a9381f177c12ba2FakePre-9986a7c303044612baa624c0a3b88cc9-1047a25b9b124fe48ef0de495e8fb451FakePre-350a31efadc3457cb2afbb612d33c53e-f6a14041c27b496aa0ac538742188dc9

    The ItemType property enables you to declare which type of data the control is going to be bound to and allows you to use strongly-typed binding inside the data-bound control.

  8. Replace the ItemTemplate content with the following code.

    (Code Snippet – Web Forms Lab - Ex01 – Costumer List Item)

    HTML

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    FakePre-5049f47cd490485f9ee438614d9984a1-f299d243c97d4dd6a936ef037c39e586FakePre-6e77e27e7adb4f6ca249e908b9529c77-0f1861c6ad994740b69668c9fbd68d5dFakePre-adb276a0fc504a0281543211635206d7-a8e3c33a55694ae3968352574b9b9415FakePre-36aa4f1a82c143c9b01e47566f1ff6dd-6298cf864e1345ada2af7918a7d31bf9 <li> <a href="CustomerDetails.aspx?id=<%#: Item.Id %>"> <%#: Item.FirstName %> <%#: Item.LastName %> </a> </li>FakePre-6ff5accc56e846c087eceb87b1f723e7-5a3777818c1742a2b8095dc39b68fcebFakePre-8249a30735bc4c0ca7364de4d9882f3d-549deb260e75400883e59996d4a1e7fdFakePre-84e756451565422abb743763e504abcf-aa12034511234e98b5ffa140af5006a1FakePre-a2412e828ac24ffd88d50e666ff312bc-73ab3d42b3c84a3e85d60b9e87cc44c5

    Setting the ItemType property causes two new typed variables to be generated in the scope of the data-binding expressions: Item and BindItem. You can use these strongly typed variables in the data-binding expressions and get the full benefits of the Visual Studio development experience.

    The “:” used in the expression will automatically HTML-encode the output to avoid security issues (for example, cross-site scripting attacks). This notation was available since .NET 4 for response writing, but now is also available in data-binding expressions.

    Note:
    The Item member works for one-way binding. If you want to perform two-way binding use the BindItem member.

    Figure 2

    IntelliSense support in strongly-typed binding

  9. Press F5 to run the solution and go to the Customers page to make sure the changes work as expected.

    Figure 3

    Listing customer details

  10. Close the browser and go back to Visual Studio.

Task 2 – Introducing Model Binding in Web Forms

In previous versions of ASP.NET Web Forms, when you wanted to perform two-way data-binding, both retrieving and updating data, you needed to use a Data Source object. This could be an Object Data Source, a SQL Data Source, a LINQ Data Source and so on. However if your scenario required custom code for handling the data, you needed to use the Object Data Source and this brought some drawbacks. For example, you needed to avoid complex types and you needed to handle exceptions when executing validation logic.

In the new version of ASP.NET Web Forms the data-bound controls support model binding. This means that you can specify select, update, insert and delete methods directly in the data-bound control to call logic from your code-behind file or from another class.

To learn about this, you will use a GridView to list the product categories using the new SelectMethod attribute. This attribute enables you to specify a method for retrieving the GridView data.

  1. Open the Products.aspx page and include a GridView. Configure the GridView as shown below to use strongly-typed bindings and enable sorting and paging.

    (Code Snippet – Web Forms Lab - Ex01 – Categories GridView)

    HTML

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <asp:GridView ID="categoriesGrid" runat="server" AutoGenerateColumns="false" ItemType="WebFormsLab.Model.Category" DataKeyNames="CategoryID"> <Columns> <asp:BoundField DataField="CategoryId" HeaderText="ID" SortExpression="CategoryId" /> <asp:BoundField DataField="CategoryName" HeaderText="Name" SortExpression="CategoryName" /> <asp:BoundField DataField="Description" HeaderText="Description" /> <asp:TemplateField HeaderText="# of Products"> <ItemTemplate><%#: Item.Products.Count %></ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>

  2. Use the new SelectMethod attribute to configure the GridView to call a GetCategories method to select the data.

    HTML

    <asp:GridView ID="categoriesGrid" runat="server"
    FakePre-0d9ae6af139a4465b53eda12aebbb58a-7494623845bc4793a450b4bf766a94a9FakePre-e011fa2251d34c7c80e691742930c01c-be8102b44af64d1f89149da4522a610e SelectMethod="GetCategories">FakePre-1a4c0785382d465182d012aad8fc9c9f-fdb0e6781aa54c76928d3b2e855be6a3FakePre-4b5d5d81546949228b4005ca52281d46-d97f083b50b248208c05834b2d82e822FakePre-5d39881897e6482caf60eb330f790db1-42d01dfd15c84858bf24bb610b30c343FakePre-384c27f3a620431487d6b564a35ea822-b15ce5e5b9ea45ce812efc1f6fa7b2c9FakePre-6638434fdbb74e199e7ad378401f427e-4174e6fdc5d741adbd6e405079085f35FakePre-d4a729cd4a404156a61923fbc4abdb9f-201a18bdb729477382a31b4adcc25ea8FakePre-55b738ec9a3747cba9de35ed4bee2487-7811a75ad8da4707ad4dff8168e580acFakePre-f578105f8d0644ebb17e847c5170d0a3-fcad727b3f294bc89d25773e3bacb1f5FakePre-8137da05803e4e83b7ee3568af9a0ee1-8a572f6e27a146d18532f918e064e071

  3. Open the Products.aspx.cs code-behind file and add the following using statements.

    (Code Snippet – Web Forms Lab - Ex01 – Namespaces)

    C#

    using System.Data.Entity; using System.Data.Entity.Infrastructure; using WebFormsLab.Model;

  4. Add a private member in the Products class and assign a new instance of ProductsContext. This property will store the Entity Framework data context that enables you to connect to the database.

    C#

    public partial class Products : System.Web.UI.Page
    FakePre-2eed07f1649447d88a674c13fb3d4c53-8b7f792f5d494190a3ca1b06a63c1c2f private ProductsContext db = new ProductsContext();FakePre-9684a5a1d5b24acc86231ad4fcf4fe12-ddc74b3a8c75422c8afabc7602bdeffa

  5. Create a GetCategories method to retrieve the list of categories using LINQ. The query will include the Products property so the GridView can show the amount of products for each category. Notice that the method returns a raw IQueryable object that represent the query to be executed later on the page lifecycle.

    (Code Snippet – Web Forms Lab - Ex01 – GetCategories)

    C#

    public IQueryable<Category> GetCategories() { var query = this.db.Categories .Include(c => c.Products); return query; }

    Note:
    In previous versions of ASP.NET Web Forms, enabling sorting and paging using your own repository logic within an Object Data Source context, required to write your own custom code and receive all the necessary parameters. Now, as the data-binding methods can return IQueryable and this represents a query still to be executed, ASP.NET can take care of modifying the query to add the proper sorting and paging parameters.

  6. Press F5 to start debugging the site and go to the Products page. You should see that the GridView is populated with the categories returned by the GetCategories method.

    Figure 4

    Populating a GridView using model binding

  7. Press SHIFT+F5 Stop debugging.

Task 3 – Value Providers in Model Binding

Model binding not only enables you to specify custom methods to work with your data directly in the data-bound control, but also allows you to map data from the page into parameters from these methods. On the method parameter, you can use value provider attributes to specify the value’s data source. For example:

  • Controls on the page
  • Query string values
  • View data
  • Session state
  • Cookies
  • Posted form data
  • View state
  • Custom value providers are supported as well

If you have used ASP.NET MVC, you will notice the model binding support is similar. Indeed, these features were taken from ASP.NET MVC and moved into the System.Web assembly to be able to use them on Web Forms as well.

In this task, you will update the GridView to filter its results by the amount of products for each category, receiving the filter parameter with model binding.

  1. Go back to the Products.aspx page.
  2. At the top of the GridView, add a Label and a ComboBox to select the number of products for each category as shown below.

    (Code Snippet – Web Forms Lab - Ex01 – Categories DropDownList)

    HTML

    <h3>Categories</h3> <asp:Label ID="Label1" runat="server" AssociatedControlID="minProductsCount">Show categories with at least this number of products:</asp:Label> <asp:DropDownList runat="server" ID="minProductsCount" AutoPostBack="true"> <asp:ListItem Value="" Text="-" /> <asp:ListItem Text="1" /> <asp:ListItem Text="3" /> <asp:ListItem Text="5" /> </asp:DropDownList> <br/>

  3. Add an EmptyDataTemplate to the GridView to show a message when there are no categories with the selected number of products.

    (Code Snippet – Web Forms Lab - Ex01 – No Categories Message)

    HTML

    <asp:GridView ID="categoriesGrid" runat="server"
    FakePre-5a292e9cddaf4bec8b14bdca1dbb331d-5a0ee2d18f9047099b620afc515765b4FakePre-e05ef1ccd914439ea0260cb4c3361a54-d1bb8b02145b4c4a901ecabfa1440b08FakePre-6a57eb3f7c7e4cb9a1c48b089aa12757-b027f48dd4d74f0f981d5f2dbee90d15FakePre-d05fd67b85814c029543087e834b066d-db82294aa7ac47189bd65fed4ddf76a0FakePre-41832eb925c5457f9b9d4e3baed71666-079849fb817d4d12821b83eac2acff2fFakePre-44c141397a6746e0ba01ea856a57be24-3e5916fe55c146b6be925cb00881c927FakePre-7bc57f1f19ba425898baaf8faa07eb45-6ec6033eacd9479b9b733dbb57dafbc2FakePre-f074e10ccdbd4b4f9894671c5e76074f-314c6da3953b4568bca068d1fe0967f8FakePre-bdb03302319d4e3c939f42bcdb909991-b958233176694c119c9407f916ac92f2FakePre-4bf8f1ff590541c2b9eea01ccfc01477-011dd03256a24b199fc4f555315aec58FakePre-020e8c8144224316842b0eb9e8267de3-22b9ae3db9274acca6ef783ed67f9075 <EmptyDataTemplate>No categories found with a product count of <%#: minProductsCount.SelectedValue %></EmptyDataTemplate>FakePre-71f2ff8121604468873488d687bdef8d-e92e95f67e274cf0b50f29a49dcb4a25

  4. Open the Products.aspx.cs code-behind and add the following using statement (shown in bold).

    C#

    using System.Data.Entity;
    FakePre-b1c7c2e0889146a5a5167e16d1281c2a-48a883920b1349aa9efad1dcb81502ceFakePre-87d06587b97e493ea9483f296cb57068-fb8d4a4f235243bcb1e2dfd7505abf7cusing System.Web.ModelBinding;

  5. Modify the GetCategories method to receive an integer minProductsCount argument and filter the returned results. To do this, replace the method with the following code.

    (Code Snippet – Web Forms Lab - Ex01 – GetCategories 2)

    C#

    public IQueryable<Category> GetCategories([Control]int? minProductsCount) { var query = this.db.Categories .Include(c => c.Products); if (minProductsCount.HasValue) { query = query.Where(c => c.Products.Count >= minProductsCount); } return query; }
    FakePre-b2f8c417ae5446eb951247d0d788479b-62de2688b69f4d67a7e7b4b7e68d7750
    

    The new [Control] attribute on the minProductsCount argument will let ASP.NET know its value must be populated using a control in the page. ASP.NET will look for any control matching the name of the argument (minProductsCount) and perform the necessary mapping and conversion to fill the parameter with the control value.

    Alternatively, the attribute provides an overloaded constructor that enables you to specify the control from where to get the value.

    Note:
    One goal of the data-binding features is to reduce the amount of code that needs to be written for page interaction.

    Apart from the [Control] value provider, you can use other model-binding providers in your method parameters. Some of them are listed in the task introduction.

  6. Press F5 to start debugging the site and go to the Products page. Select a number of products in the drop-down list and notice how the GridView is now updated.

    Figure 5

    Filtering the GridView with a drop-down list value

  7. Stop debugging.

Task 4 – Using Model Binding for Filtering

In this task, you will add a second, child GridView to show the products within the selected category.

  1. Open the Products.aspx page and update the categories GridView to auto-generate the Select button.

    HTML

    <asp:GridView ID="categoriesGrid" runat="server"
    FakePre-6bda8606ddf64b37a14a9a53a154feaf-b69c5d9766384da9b5c4e65c8ffe25e7FakePre-3320a2657e494b59beb2c4827c14f7b5-26ad693191b242e396e23b54096f8836FakePre-c71e7f479d26449282b1f70e8b401034-36c730bf8be54296b1e1a1f2a030f069 AutoGenerateSelectButton="true">

  2. Add a second GridView named productsGrid at the bottom. Set the ItemType to WebFormsLab.Model.Product, the DataKeyNames to ProductId and the SelectMethod to GetProducts. Set AutoGenerateColumns to false and add the columns for ProductId, ProductName, Description and UnitPrice.

    (Code Snippet – Web Forms Lab - Ex01 – Products GridView)

    HTML

    <h3>Products</h3> <asp:GridView ID="productsGrid" runat="server" CellPadding="4" AutoGenerateColumns="false" ItemType="WebFormsLab.Model.Product" DataKeyNames="ProductId" SelectMethod="GetProducts"> <Columns> <asp:BoundField DataField="ProductId" HeaderText="ID" /> <asp:BoundField DataField="ProductName" HeaderText="Name" /> <asp:BoundField DataField="Description" HeaderText="Description" HtmlEncode="false" /> <asp:BoundField DataField="UnitPrice" HeaderText="Price" /> </Columns> <EmptyDataTemplate> Select a category above to see its products </EmptyDataTemplate> </asp:GridView>

  3. Open the Products.aspx.cs code-behind file. Implement the GetProducts method to receive the category ID from the category GridView and filter the products. Model binding will set the parameter value using the selected row in the categoriesGrid. Since the argument name and control name do not match, you should specify the name of the control in the Control value provider.

    (Code Snippet – Web Forms Lab - Ex01 – GetProducts)

    C#

    public IEnumerable<WebFormsLab.Model.Product> GetProducts([Control("categoriesGrid")]int? categoryId) { return this.db.Products.Where(p => p.CategoryId == categoryId); }

    Note:
    This approach makes it easier to unit test these methods. On a unit test context, where Web Forms is not executing, the [Control] attribute will not perform any specific action.

  4. Open the Products.aspx page and locate the products GridView. Update the products GridView to show a link for editing the selected product.

    (Code Snippet – Web Forms Lab - Ex01 – Edit Product Link)

    HTML

    <h3>Products</h3>
    FakePre-3425a67d114144d1a61b10432129b3c4-afeeef40d569462c80fae90e2e6f125bFakePre-fcb7827c123b4363be0866644dead8e6-7fb65ce6a1f3474aa1ae289597e56338FakePre-cb6cc7448d1b41338997ad77a336ce03-6f9ae962d58f4b7594eb10f406f0137bFakePre-d3a2b49e3b864e64b4e2a93ba1f094a1-6132aaf76715440fbadd0eb88f3b4a94FakePre-1f310c86fdd84c04be72039147914e8a-3810eddb7a644d72a5de4368f5dcb5f3FakePre-7f4e776654cf45be921b0b998daa953a-5f19a2bae3b64bbabd57e055fa894afaFakePre-1119eee5f8014a1e94e1a2c401cf5ecd-e1f701857f3d428db7369931e111673c <asp:TemplateField> <ItemTemplate><a href="ProductDetails.aspx?productId=<%#: Item.ProductId %>">View</a></ItemTemplate> </asp:TemplateField>FakePre-55aba4e2f258437297c9e728ffda582b-f1b97c3382ef4f389c39ff62c6f6e59cFakePre-a8948016bd164ee3a0b6d44e1dc8ee4d-0b5dcc61900a46738b572356ac4c8b8dFakePre-aacf59ba106e4b4d8883763d4f6f3dd4-1963909e36a7401e9edad605eb722d03FakePre-5daa19a4cdec4bc39711a0bc90e27281-ada75fdbed6047ab996041af92f29b2eFakePre-58f9a6a258f14243b479bc2d7156532b-80c3b463144f41b3a6b8045c73a6b6edFakePre-5469f4114a5647d5a016d769966cf0b7-17788b0bd7bb4f749ee8844622725c52FakePre-79acf473bb634ae1960ce30ac828e12d-08ed6a51b2c645de889765769da9b67bFakePre-4d64b2237dee4c70979c78fd5fbd49de-ac924ecc26fa4d25affac657b1f43ad0FakePre-c7caa3770e9042208ea3b63c8b28754f-281a4f1208b94a338d299055ea8329ba

  5. Open the ProductDetails.aspx page code-behind and add the SelectProduct method as shown below.

    Notice that the [QueryString] attribute is used to fill the method parameter from a productId parameter in the query string.

    (Code Snippet – Web Forms Lab - Ex01 – SelectProduct Method)

    C#

    public Product SelectProduct([QueryString]int? productId) { return this.db.Products.Find(productId); }

  6. Press F5 to start debugging the site and go to the Products page. Select any category from the categories GridView and notice that the products GridView is updated.

    Figure 6

    Showing products from the selected category

  7. Click the View link on a product to open the ProductDetails.aspx page.

    Notice that the page is retrieving the product with the SelectMethod using the productId parameter from the query string.

    Figure 7

    Viewing the product details

    Note:
    The ability to type an HTML description will be implemented in the next exercise.

Task 5 – Using Model Binding for Update Operations

In the previous task, you have used model binding mainly for selecting data, in this task you will learn how to use model binding in update operations.

You will update the categories GridView to let the user update categories.

  1. Open the Products.aspx page and update the categories GridView to auto-generate the Edit button and use the new UpdateMethod attribute to specify an UpdateCategory method to update the selected item.

    (Code Snippet – Web Forms Lab - Ex01 – Enable Categories Update)

    HTML

    <asp:GridView ID="categoriesGrid" runat="server"
    FakePre-be608b26acad41939a6fe2727364bf53-8e6085de703a472d824df90147045f77FakePre-5792a0506a1c4031b9d285aefae25e56-9d9f04a772db4c83b69d00ec9c912d38FakePre-a7346512891c456fb4c797d17a452ff6-c301f57d11004c78b6c49f4e3e94f90aFakePre-47b98d8b1c484ab997e4f58fd54794ae-c3623a3ad7a84753be1f1409c2a41970FakePre-7dd6a65ddb7f476bb77c3ce3a89c890a-982301efd5684c71a6b772b279604551 AutoGenerateEditButton="true" UpdateMethod="UpdateCategory">

    The DataKeyNames attribute in the GridView define which are the members that uniquely identify the model-bound object and therefore, which are the parameters the update method should at least receive.

  2. Open the Products.aspx.cs code-behind file and implement the UpdateCategory method. The method should receive the category ID to load the current category, populate the values from the GridView and then update the category.

    (Code Snippet – Web Forms Lab - Ex01 – UpdateCategory)

    C#

    public void UpdateCategory(int categoryId) { var category = this.db.Categories.Find(categoryId); TryUpdateModel(category); if (ModelState.IsValid) { this.db.SaveChanges(); } }

    The new TryUpdateModel method in the Page class is responsible of populating the model object using the values from the controls in the page. In this case, it will replace the updated values from the current GridView row being edited into the category object.

    Note:
    The next exercise will explain the usage of the ModelState.IsValid for validating the data entered by the user when editing the object.

  3. Run the site and go to the Products page. Edit a category. Type a new name and then click Update to persist the changes.

    Figure 8

    Editing categories