Archives

Archives / 2008 / June
  • An Introduction to xUnit.net for ASP.NET MVC Developers

    The purpose of this blog entry is to provide you with an introduction to the xUnit.net unit testing framework, the newest unit testing framework for the .NET framework. I want to demonstrate how you can use the xUnit.net framework when practicing test-driven development. In particular, I focus on using xUnit.net when building an ASP.NET MVC application.

  • ASP.NET MVC Tip #11 – Use Standard Controller Action Names

    In this tip, I recommend that you use standard names for your controller actions in order to make your code more transparent to other developers.

    Adopting naming conventions makes your code easier to read for other developers and your future self. Naming conventions also saves you time so that you can prevent endless debates about the “right” way to name something. In this tip, I recommend standard names for ASP.NET MVC controller actions.

    Here is the table of suggested standard names for controller actions:

    Action

    Sample URL

    Description

    Details

    /Product/Details/5

    Displays a single resource such as a database record. For example, displays a single Product with an Id of 5.

    Index

    /Product/Index

    Displays a collection of resources. For example, displays all of the products in the products database table.

    Create

    /Product/Create

    Displays a form for creating a new resource. For example, displays a form for creating a new product.

    Insert

    /Product/Insert

    Inserts a new resource into the database. Typically, you redirect to another action after performing an Insert.

    Edit

    /Product/Edit/5

    Displays a form for editing an existing resource. For example, displays a form for editing a product with an Id of 5.

    Update

    /Product/Update/5

    Updates existing resources in the database. Typically, you redirect to another action after performing an Update.

    Destroy

    /Product/Destroy/5

    Displays a page that confirms whether or not you want to delete a resource from the database.

    Delete

    /Product/Delete/5

    Deletes a resource from the database. Typically, you redirect to another action after performing a Delete.

    Login

    /Home/Login

    Displays a login form.

    Logout

    /Home/Logout

    Logs out a user. Typically, you redirect to another action after performing a Logout.

    Authenticate

    /Home/Authenticate

    Authenticates a user name and password. Typically, you redirect to another action after performing an Authenticate.

    I based these action names (very roughly) on Adam Tybor’s naming conventions for his Simply Restful Route Handler included in the MvcContrib project at:

    http://www.codeplex.com/MVCContrib/Wiki/View.aspx?title=SimplyRestfulRouting&referringTitle=Documentation

    However, there are significant differences between Adam Tybor’s conventions and the ones that I recommend here. Let me explain the changes.

    First, Adam Tybor takes advantage of the different HTTP verbs to indicate the controller action to execute (he is being a REST purist). For example, Adam Tybor recommends that the same URL be used to either delete or update a resource:

    /Product/34

    When this URL is requested with an HTTP PUT then an existing record is updated. When the same URL is requested with an HTTP DELETE then an existing record is deleted. Adam Tybor’s reasoning here makes sense. His recommendation fits the intended purpose of these HTTP verbs. However, Adam Tybor’s recommendations do not work with the default ASP.NET MVC route. Therefore, I recommend that you use distinct URLs when performing deletes and updates:

    /Product/Update/34

    /Product/Delete/34

    If you use these naming conventions, then you can use the default ASP.NET MVC route table.

    One other important difference between the action naming convention that I recommend here and other naming conventions that I have seen concern the naming of the actions for creating a new resource. Typically, I see the following action names used when creating a new resource:

    /Product/New

    /Product/Create

    Typically, the New action is used to display a form for creating a new resource and the Create action actually performs the database Insert operation.

    The problem with using an action named New is that this action name conflicts with a C# and Visual Basic .NET keyword. When creating a controller action named New in a Visual Basic .NET application, you must always remember to escape the name like this [New]. Constantly escaping a name gets tiresome quickly and it is also confusing to people who don't understand the meaning of the square brackets. Therefore, I recommend the pair Create and Insert instead of the pair New and Create.

    You might notice that I have tried to align actions that (typically) perform a database operation with their corresponding SQL statement name. Therefore, the following URLs are used to display forms:

    /Product/Create

    /Product/Edit/1

    /Product/Destroy/1

    And the following URLs are used to perform database operations:

    /Product/Insert

    /Product/Update/1

    /Product/Delete/1

    Proposing naming conventions is always risky because developers have such strong personal opinions on the right ways to name things. However, I hope this tip can act as a starting point for whatever naming conventions that you develop.

  • ASP.NET MVC Tip #9 – Create a GridView View User Control

    In this tip, I show you how to build an ASP.NET MVC View User Control that accepts a set of database records and renders the records in an HTML table automatically. The advantage of using a View User Control is that you can customize the rendering of particular columns.

    In yesterday’s tip, I explained how you can create a new HTML helper that renders a set of database records in an HTML table. In other words, I showed one method for simulating a GridView control in ASP.NET MVC. In today’s tip, I am going to show you a second method of simulating a GridView.

    In today’s tip, I explain how you can simulate a GridView control by using an ASP.NET MVC View User Control. An ASP.NET MVC View User Control is similar to an ASP.NET User Control with one important difference. Just like an ASP.NET MVC View, a View User Control can accept strongly typed view data. We are going to create a View User Control that accepts IEnumerable view data.

    The GridView View User Control is contained in Listing 1.

    Listing 1 – GridView.ascx (vb)

       1: <%@ Control Language="VB" AutoEventWireup="false" CodeBehind="GridView.ascx.vb" Inherits="Tip9.GridView" %>
       2: <%@ Import Namespace="System.Reflection" %>
       3:  
       4: <%-- Show the Headers --%>
       5: <table class="gridView">
       6: <thead>
       7: <tr>
       8:     <% For Each prop As PropertyInfo In Me.Columns%>
       9:         <th><%= prop.Name %></th>   
      10:     <% Next%>
      11: </tr>
      12: </thead>
      13:  
      14: <%-- Show the Rows --%>
      15: <tbody>
      16:  
      17:     <%  For Each row In Me.Rows%>
      18:        <tr class="<%= Me.FlipCssClass( "item", "alternatingItem") %>">
      19:        
      20:        <%-- Show Each Column --%>
      21:        <%  For Each prop As PropertyInfo In Me.Columns%>
      22:             <td>
      23:             <% Dim typeCode = Type.GetTypeCode(prop.PropertyType)%>
      24:  
      25:  
      26:             <%-- String Columns --%>
      27:             <%  If typeCode = typeCode.String Then %>
      28:                
      29:                 <%= GetColumnValue(row, prop.Name)%>
      30:             
      31:             <% End If%>
      32:  
      33:             <%-- DateTime Columns --%>
      34:             <% If typeCode = typeCode.DateTime Then%>
      35:                 
      36:                 <%= GetColumnValue(row, prop.Name, "{0:D}")%>
      37:             
      38:             <% End If%>
      39:  
      40:  
      41:             <%-- Decimal Columns --%>
      42:             <%  If typeCode = typeCode.Decimal Then%>
      43:                 
      44:                 <%= GetColumnValue(row, prop.Name, "{0:c}") %>
      45:             
      46:             <% End If%>
      47:  
      48:  
      49:             <%-- Boolean Columns --%>
      50:             <% If typeCode = typeCode.Boolean Then%>
      51:                 <% If Me.GetColumnValue(row, prop.Name) = True Then%>
      52:                    <input type="checkbox" disabled="disabled" checked="checked" />                   
      53:                    <%  Else%>
      54:                    <input type="checkbox" disabled="disabled" />
      55:                 <% End If%>
      56:             <% End If%>
      57:             
      58:             
      59:             <%-- Integer Columns --%>
      60:             <% If TypeCode = TypeCode.Int32 Then%>
      61:                 
      62:                 <%= GetColumnValue(row, prop.Name)%>
      63:             
      64:             <% End If%>
      65:             
      66:             </td>
      67:        <% next %>
      68:         </tr>
      69:     <% next %>
      70:  </tbody>   
      71:  </table>
      72:  
      73:  

    Listing 1 – GridView.ascx (c#)

       1: <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="GridView.ascx.cs" Inherits="Tip9.Views.Home.GridView" %>
       2: <%@ Import Namespace="System.Reflection" %>
       3:  
       4: <%-- Show the Headers --%>
       5: <table class="gridView">
       6: <thead>
       7: <tr>
       8:     <% foreach (PropertyInfo prop in this.Columns)
       9:        { %>
      10:         <th><%= prop.Name %></th>   
      11:     <% } %>
      12: </tr>
      13: </thead>
      14:  
      15: <%-- Show the Rows --%>
      16: <tbody>
      17:  
      18:     <% foreach (object row in this.Rows)
      19:        { %>
      20:        <tr class="<%= this.FlipCssClass( "item", "alternatingItem") %>">
      21:        
      22:        <%-- Show Each Column --%>
      23:        <% foreach (PropertyInfo prop in this.Columns)
      24:           { %>
      25:             <td>
      26:             <% var typeCode = Type.GetTypeCode(prop.PropertyType); %>
      27:  
      28:  
      29:             <%-- String Columns --%>
      30:             <% if (typeCode == TypeCode.String) 
      31:                { %>
      32:                
      33:                 <%= GetColumnValue(row, prop.Name)%>
      34:             
      35:             <% } %>
      36:  
      37:             <%-- DateTime Columns --%>
      38:             <% if (typeCode == TypeCode.DateTime) 
      39:                { %>
      40:                 
      41:                 <%= GetColumnValue(row, prop.Name, "{0:D}")%>
      42:             
      43:             <% } %>
      44:  
      45:  
      46:             <%-- Decimal Columns --%>
      47:             <% if (typeCode == TypeCode.Decimal) 
      48:                { %>
      49:                 
      50:                 <%= GetColumnValue(row, prop.Name, "{0:c}") %>
      51:             
      52:             <% } %>
      53:  
      54:  
      55:             <%-- Boolean Columns --%>
      56:             <% if (typeCode == TypeCode.Boolean) 
      57:                { %>
      58:                 <% if ((bool)(this.GetColumnValue(row, prop.Name)))
      59:                    { %>
      60:                    <input type="checkbox" disabled="disabled" checked="checked" />                   
      61:                    <% }
      62:                    else
      63:                    { %>
      64:                    <input type="checkbox" disabled="disabled" />
      65:                 <% } %>
      66:             <% } %>
      67:             
      68:             
      69:             <%-- Integer Columns --%>
      70:             <% if (typeCode == TypeCode.Int32) 
      71:                { %>
      72:                 
      73:                 <%= GetColumnValue(row, prop.Name)%>
      74:             
      75:             <% } %>
      76:             
      77:             </td>
      78:        <% } %>
      79:         </tr>
      80:     <% } %>
      81:  </tbody>   
      82:  </table>
      83:  

    Notice that the GridView.ascx file contains two loops. The first loop iterates through the table headers and the second loop iterates through the table rows.

    A series of IF statements are used to display a particular column. Depending on the type of column -- Integer, String, Decimal, DateTime, Boolean – a different template is used to display the column value. For example, in the case of a Boolean column, a checkbox is used to display the column value (see Figure 1). You can, of course, customize the appearance of any of these columns by modifying the HTML.

    Figure 1 -- GridView

    clip_image002

    The code-behind file for the GridView View User Control is contained in Listing 2. Notice that the View User Control has a generic constructor that accepts IEnumerable view data. It also exposes several utility properties and methods. For example, the Columns property returns information about all of the database table columns (this information is retrieved through reflection). The Rows property returns all of the database table rows.

    Listing 2 – GridView.ascx.vb (vb)

       1: Imports System.Reflection
       2:  
       3: Partial Public Class GridView
       4:     Inherits System.Web.Mvc.ViewUserControl
       5:  
       6:  
       7:     Protected ReadOnly Property Columns() As PropertyInfo()
       8:         Get
       9:             Dim e As IEnumerator = ViewData.Model.GetEnumerator()
      10:             e.MoveNext()
      11:             Dim firstRow As Object = e.Current
      12:             If firstRow Is Nothing Then
      13:                 Throw New Exception("No data passed to GridView User Control.")
      14:             End If
      15:             Return firstRow.GetType().GetProperties()
      16:         End Get
      17:     End Property
      18:  
      19:     Protected ReadOnly Property Rows() As IEnumerable
      20:         Get
      21:             Return ViewData.Model
      22:         End Get
      23:     End Property
      24:  
      25:  
      26:     Protected Function GetColumnValue(ByVal row As Object, ByVal columnName As String) As Object
      27:         Return DataBinder.Eval(row, columnName)
      28:     End Function
      29:  
      30:     Protected Function GetColumnValue(ByVal row As Object, ByVal columnName As String, ByVal format As String) As Object
      31:         Return DataBinder.Eval(row, columnName, format)
      32:     End Function
      33:  
      34:  
      35:     Dim flip As Boolean = False
      36:  
      37:     Protected Function FlipCssClass(ByVal className As String, ByVal alternativeClassName As String) As String
      38:         flip = Not flip
      39:         Return IIf(flip, className, alternativeClassName)
      40:     End Function
      41:  
      42:  
      43: End Class

    Listing 2 – GridView.ascx.cs (c#)

       1: using System;
       2: using System.Collections;
       3: using System.Collections.Generic;
       4: using System.Linq;
       5: using System.Web;
       6: using System.Web.UI;
       7: using System.Web.Mvc;
       8: using System.Reflection;
       9:  
      10: namespace Tip9.Views.Home
      11: {
      12:     public partial class GridView : System.Web.Mvc.ViewUserControl<IEnumerable>
      13:     {
      14:         protected PropertyInfo[] Columns
      15:         {
      16:             get 
      17:             {
      18:                 var e = ViewData.Model.GetEnumerator();
      19:                 e.MoveNext();
      20:                 object firstRow = e.Current;
      21:                 if (firstRow == null)
      22:                 {
      23:                     throw new Exception("No data passed to GridView User Control.");
      24:                 }
      25:                 return firstRow.GetType().GetProperties();
      26:             }
      27:         }
      28:  
      29:         protected IEnumerable Rows
      30:         {
      31:             get { return ViewData.Model; }
      32:         }
      33:  
      34:  
      35:         protected object GetColumnValue(object row, string columnName)
      36:         {
      37:             return DataBinder.Eval(row, columnName);
      38:         }
      39:  
      40:         protected object GetColumnValue(object row, string columnName, string format)
      41:         {
      42:             return DataBinder.Eval(row, columnName, format); 
      43:         }
      44:  
      45:  
      46:         bool flip = false;
      47:         protected string FlipCssClass(string className, string alternativeClassName)
      48:         {
      49:             flip = !flip;
      50:             return flip ? className : alternativeClassName;
      51:         }
      52:  
      53:     }
      54: }

    You can use the GridView control in a View Page by calling the Html.RenderUserControl() method. The View Page in Listing 3 renders the GridView View User Control. Notice that the page contains a CSS Style Sheet. This Style Sheet is used to customize the appearance of the table rendered by the GridView. For example, the alternating CSS class is used to format alternating GridView rows.

    Listing 3 – Index.aspx (vb)

       1: <%@ Page Language="VB" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="Tip9.Index" %>
       2:  
       3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
       4:  
       5: <html xmlns="http://www.w3.org/1999/xhtml" >
       6: <head id="Head1" runat="server">
       7:     <title>Index</title>
       8:     <style type="text/css">
       9:     
      10:     
      11:     .gridView 
      12:     {
      13:         border-collapse: collapse;
      14:     }
      15:     
      16:     .gridView th, .gridView td
      17:     {
      18:         padding: 5px;
      19:     }
      20:     
      21:     .gridView th
      22:     {
      23:         border-bottom: double 3px black;
      24:     }
      25:     
      26:     .gridView td
      27:     {
      28:         border-bottom: solid 1px black;
      29:     }
      30:     
      31:     .alternatingItem
      32:     {
      33:         background-color: lightgrey;
      34:     }
      35:     
      36:     </style>
      37: </head>
      38: <body>
      39:     <div>
      40:     
      41:     
      42:     <%= Html.RenderUserControl("~/Views/Home/GridView.ascx", ViewData.Model) %>
      43:     
      44:     </div>
      45: </body>
      46: </html>

    Listing 3 – Index.aspx (C#)

       1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Tip9.Views.Home.Index" %>
       2:  
       3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
       4:  
       5: <html xmlns="http://www.w3.org/1999/xhtml" >
       6: <head runat="server">
       7:     <title>Index</title>
       8:     <style type="text/css">
       9:     
      10:     
      11:     .gridView 
      12:     {
      13:         border-collapse: collapse;
      14:     }
      15:     
      16:     .gridView th, .gridView td
      17:     {
      18:         padding: 5px;
      19:     }
      20:     
      21:     .gridView th
      22:     {
      23:         border-bottom: double 3px black;
      24:     }
      25:     
      26:     .gridView td
      27:     {
      28:         border-bottom: solid 1px black;
      29:     }
      30:     
      31:     .alternatingItem
      32:     {
      33:         background-color: lightgrey;
      34:     }
      35:     
      36:     </style>
      37: </head>
      38: <body>
      39:     <div>
      40:     
      41:     
      42:     <%= Html.RenderUserControl("~/Views/Home/GridView.ascx", ViewData.Model) %>
      43:     
      44:     </div>
      45: </body>
      46: </html>

    The view data is supplied by the controller in LIsting 4. This controller happens to use a LINQ to SQL query to retrieve the database data. However, the GridView is perfectly compatible with data retrieved through ADO.NET, NHibernate, or whatever. The GridView expects IEnumerable data. As long as you pass the GridView something that is IEnumerable, it will be happy.

    Listing 4 -- HomeController.vb (vb)

       1: Public Class HomeController
       2:     Inherits System.Web.Mvc.Controller
       3:  
       4:     Private _db As New MovieDataContext()
       5:  
       6:     Function Index()
       7:         Return View(_db.Movies)
       8:     End Function
       9:  
      10: End Class

    Listing 4 -- HomeController.cs (C#)

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Linq;
       4: using System.Web;
       5: using System.Web.Mvc;
       6: using Tip9.Models;
       7:  
       8: namespace Tip9.Controllers
       9: {
      10:     public class HomeController : Controller
      11:     {
      12:         private MovieDataContext _db = new MovieDataContext();
      13:  
      14:  
      15:         public ActionResult Index()
      16:         {
      17:             return View(_db.Movies);
      18:         }
      19:     }
      20: }

    I prefer the method of displaying a grid of database data described in this blog entry over the method described in yesterday’s tip. Unlike the method used in yesterday’s tip, today’s method enables you to completely customize the appearance of the GridView.

    You can download the code for the GridView by clicking the following link. The download includes the code in both C# and VB.NET versions.

    Download the Code

  • ASP.NET MVC Tip #8 – Create an ASP.NET MVC GridView Helper Method

    In this tip, you learn how to extend the ASP.NET MVC framework with a new helper method that displays an HTML table of database data.

    Currently, the ASP.NET MVC framework does not include anything that is the direct equivalent of the ASP.NET Web Forms GridView control. If you want to display a table of database data then you must write out all of the HTML and inline script each and every time that you want to display the data. In this tip, I show you how to add a GridView() extension method to the HtmlHelper class.

    An extension method is a method added to one class by another class. You can use extension methods to give existing classes additional super powers. In our case, we want to give the HtmlHelper class, the class that you use in an MVC View Page, a new GridView() method that renders an HTML table of database data.

    You create an extension method in slightly different ways when working with Visual Basic .NET and when working with C#. You create an extension method with Visual Basic .NET by creating a module and decorating functions in the module with the <Extension> attribute. You create an extension method with C# by creating a static class and using the keyword this with the first parameter of each extension method exposed by the static class.

    The code for the GridView() extension methods is contained in Listing 1.

    Listing 1 – GridExtensions.vb (VB)

       1: Imports System
       2: Imports System.Text
       3: Imports System.Collections.Generic
       4: Imports System.Linq
       5: Imports System.Data.Linq.Mapping
       6: Imports System.Data.Linq
       7: Imports System.Web.UI
       8: Imports System.Web.Mvc
       9: Imports System.Web
      10: Imports System.Runtime.CompilerServices
      11:  
      12:  
      13: Namespace Helpers
      14:  
      15:     Public Module GridExtensions
      16:  
      17:         <Extension()> _
      18:         Public Function GridView(ByVal htmlHelper As HtmlHelper, ByVal table As ITable) As String
      19:             Return GridView(htmlHelper, table, Nothing, New GridViewOptions())
      20:         End Function
      21:  
      22:         <Extension()> _
      23:         Public Function GridView(ByVal htmlHelper As HtmlHelper, ByVal table As ITable, ByVal headers As String()) As String
      24:             Return GridView(htmlHelper, table, headers, New GridViewOptions())
      25:         End Function
      26:  
      27:         <Extension()> _
      28:         Public Function GridView(ByVal htmlHelper As HtmlHelper, ByVal table As ITable, ByVal includeLinks As Boolean) As String
      29:             Return GridView(htmlHelper, table, Nothing, includeLinks)
      30:         End Function
      31:  
      32:         <Extension()> _
      33:         Public Function GridView(ByVal htmlHelper As HtmlHelper, ByVal table As ITable, ByVal headers As String(), ByVal includeLinks As Boolean) As String
      34:             Dim options As New GridViewOptions()
      35:             If Not includeLinks Then
      36:                 options.ShowViewButton = False
      37:                 options.ShowEditButton = False
      38:                 options.ShowDeleteButton = False
      39:             End If
      40:             Return GridView(htmlHelper, table, Nothing, options)
      41:         End Function
      42:  
      43:         <Extension()> _
      44:         Public Function GridView(ByVal htmlHelper As HtmlHelper, ByVal table As ITable, ByVal headers As String(), ByVal options As GridViewOptions) As String
      45:             ' Show edit column?
      46:             Dim showEditColumn As Boolean = options.ShowViewButton Or options.ShowEditButton Or options.ShowDeleteButton
      47:  
      48:             ' Get identity column name
      49:             Dim identityColumnName As String = GridExtensions.GetIdentityColumnName(table)
      50:  
      51:             ' Get column names and headers
      52:             Dim columnNames = GridExtensions.GetColumnNames(table)
      53:             If IsNothing(headers) Then
      54:                 headers = columnNames
      55:             End If
      56:  
      57:             ' Open table
      58:             Dim sb As New StringBuilder()
      59:             sb.AppendLine("<table>")
      60:  
      61:             ' Create Header Row
      62:             sb.AppendLine("<thead>")
      63:             sb.AppendLine("<tr>")
      64:             If showEditColumn Then
      65:                 sb.Append("<th></th>")
      66:             End If
      67:             For Each header As String In headers
      68:                 sb.AppendFormat("<th>{0}</th>", header)
      69:             Next
      70:             sb.AppendLine("</tr>")
      71:             sb.AppendLine("</thead>")
      72:  
      73:             ' Create Data Rows
      74:             sb.AppendLine("<tbody>")
      75:             sb.AppendLine("<tr>")
      76:             Dim row As Object
      77:             For Each row In table
      78:                 If showEditColumn Then
      79:                     Dim identityValue As Integer = CType(DataBinder.GetPropertyValue(row, identityColumnName), Integer)
      80:                     sb.Append("<td><small>")
      81:                     If (options.ShowViewButton) Then
      82:                         sb.Append(htmlHelper.ActionLink(options.ViewButtonText, options.ViewAction, New With {.Id = identityValue}))
      83:                     End If
      84:                     sb.Append("&nbsp;")
      85:                     If options.ShowEditButton Then
      86:                         sb.Append(htmlHelper.ActionLink(options.EditButtonText, options.EditAction, New With {.Id = identityValue}))
      87:                         sb.Append("&nbsp;")
      88:                     End If
      89:                     If options.ShowDeleteButton Then
      90:                         sb.Append(htmlHelper.ActionLink(options.DeleteButtonText, options.DeleteAction, New With {.Id = identityValue}))
      91:                         sb.Append("</small></td>")
      92:                     End If
      93:                 End If
      94:                 For Each columnName As String In columnNames
      95:                     Dim value As String = DataBinder.GetPropertyValue(row, columnName).ToString()
      96:                     sb.AppendFormat("<td>{0}</td>", HttpUtility.HtmlEncode(value))
      97:                 Next
      98:                 sb.AppendLine("</tr>")
      99:             Next
     100:             sb.AppendLine("</tbody>")
     101:  
     102:             sb.AppendLine("</table>")
     103:             Return sb.ToString()
     104:         End Function
     105:  
     106:         Public Function GetColumnNames(ByVal table As ITable) As String()
     107:             Return table.Context.Mapping.GetMetaType(table.ElementType).PersistentDataMembers.Select(Function(m) m.Name).ToArray()
     108:         End Function
     109:  
     110:         Public Function GetIdentityColumnName(ByVal table As ITable) As String
     111:             Return table.Context().Mapping().GetMetaType(table.ElementType).DBGeneratedIdentityMember().Name
     112:         End Function
     113:     End Module
     114:  
     115: End Namespace

    Listing 1 – GridExtensions.cs (C#)

       1: using System;
       2: using System.Text;
       3: using System.Collections.Generic;
       4: using System.Linq;
       5: using System.Data.Linq.Mapping;
       6: using System.Data.Linq;
       7: using System.Web.UI;
       8: using System.Web.Mvc;
       9: using System.Web;
      10:  
      11: namespace Tip8.Helpers
      12: {
      13:     public static class GridExtensions
      14:     {
      15:  
      16:         public static string GridView(this HtmlHelper htmlHelper, ITable table)
      17:         {
      18:             return GridView(htmlHelper, table, null, new GridViewOptions());
      19:         }
      20:  
      21:         public static string GridView(this HtmlHelper htmlHelper, ITable table, string[] headers)
      22:         {
      23:             return GridView(htmlHelper, table, headers, new GridViewOptions());
      24:         }
      25:  
      26:         public static string GridView(this HtmlHelper htmlHelper, ITable table, bool includeLinks)
      27:         {
      28:             return GridView(htmlHelper, table, null, includeLinks);
      29:         }
      30:  
      31:         public static string GridView(this HtmlHelper htmlHelper, ITable table, string[] headers, bool includeLinks)
      32:         {
      33:             var options = new GridViewOptions();
      34:             if (!includeLinks)
      35:             {
      36:                 options.ShowViewButton = false;
      37:                 options.ShowEditButton = false;
      38:                 options.ShowDeleteButton = false;
      39:             }
      40:             return GridView(htmlHelper, table, null, options);
      41:         }
      42:  
      43:         public static string GridView(this HtmlHelper htmlHelper, ITable table, string[] headers, GridViewOptions options)
      44:         {
      45:             // Show edit column?
      46:             bool showEditColumn = options.ShowViewButton || options.ShowEditButton || options.ShowDeleteButton; 
      47:  
      48:             // Get identity column name
      49:             string identityColumnName = GridExtensions.GetIdentityColumnName(table);
      50:  
      51:             // Get column names and headers
      52:             var columnNames = GridExtensions.GetColumnNames(table);
      53:             if (headers == null)
      54:                 headers = columnNames;
      55:  
      56:             // Open table
      57:             var sb = new StringBuilder();
      58:             sb.AppendLine("<table>");
      59:  
      60:             // Create Header Row
      61:             sb.AppendLine("<thead>");
      62:             sb.AppendLine("<tr>");
      63:             if (showEditColumn)
      64:                 sb.Append("<th></th>");
      65:             foreach (String header in headers)
      66:                 sb.AppendFormat("<th>{0}</th>", header);
      67:             sb.AppendLine("</tr>");
      68:             sb.AppendLine("</thead>");
      69:  
      70:             // Create Data Rows
      71:             sb.AppendLine("<tbody>");
      72:             sb.AppendLine("<tr>");
      73:             foreach (Object row in table)
      74:             {
      75:                 if (showEditColumn)
      76:                 {
      77:                     int identityValue = (int)DataBinder.GetPropertyValue(row, identityColumnName);
      78:                     sb.Append("<td><small>");
      79:                     if (options.ShowViewButton)
      80:                     {
      81:                         sb.Append(htmlHelper.ActionLink(options.ViewButtonText, options.ViewAction, new { Id = identityValue }));
      82:                         sb.Append("&nbsp;");
      83:                     }
      84:                     if (options.ShowEditButton)
      85:                     {
      86:                         sb.Append(htmlHelper.ActionLink(options.EditButtonText, options.EditAction, new { Id = identityValue }));
      87:                         sb.Append("&nbsp;");
      88:                     }
      89:                     if (options.ShowDeleteButton)
      90:                     {
      91:                         sb.Append(htmlHelper.ActionLink(options.DeleteButtonText, options.DeleteAction, new { Id = identityValue }));
      92:                     }
      93:                     sb.Append("</small></td>");
      94:                 }
      95:                 foreach (string columnName in columnNames)
      96:                 {
      97:                     string value = DataBinder.GetPropertyValue(row, columnName).ToString();
      98:                     sb.AppendFormat("<td>{0}</td>", HttpUtility.HtmlEncode(value));
      99:                 }
     100:                 sb.AppendLine("</tr>");
     101:             }
     102:             sb.AppendLine("</tbody>");
     103:  
     104:             sb.AppendLine("</table>");
     105:             return sb.ToString();
     106:         }
     107:  
     108:         public static string[] GetColumnNames(ITable table)
     109:         {
     110:             return table
     111:                 .Context
     112:                 .Mapping
     113:                 .GetMetaType(table.ElementType)
     114:                 .PersistentDataMembers.Select(m => m.Name)
     115:                 .ToArray();
     116:         }
     117:  
     118:         public static string GetIdentityColumnName(ITable table)
     119:         {
     120:             return table
     121:                 .Context
     122:                 .Mapping
     123:                 .GetMetaType(table.ElementType)
     124:                 .DBGeneratedIdentityMember
     125:                 .Name;
     126:         }
     127:     }
     128:  
     129: }

    Listing 1 contains multiple version of the GridView() method. Each version of the GridView() method accepts a different set of parameters. For example, the first version of the GridView() method accepts a LINQ to SQL table and renders all of the columns and rows from the table. Other versions of the GridView() method enable you to customize the GridView headers and edit links.

    The MVC view in Listing 2 demonstrates multiple ways of calling the GridView() method to display the contents of a database table.

    Listing 2 – Index.aspx (VB)

       1: <%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="Tip8.Index" %>
       2: <%@ Import Namespace="Tip8.Helpers" %>
       3:  
       4: <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
       5:    
       6:    <h1>Simple Grid</h1>
       7:    
       8:    <%= Html.GridView(ViewData.Model) %>
       9:  
      10:    <h1>Simple Grid without Links</h1>
      11:    
      12:    <%= Html.GridView(ViewData.Model, false) %>
      13:  
      14:    <h1>Simple Grid with Custom Headers</h1>
      15:    
      16:    <%=Html.GridView(ViewData.Model, New String() {"AA", "BB", "CC", "DD"})%>
      17:    
      18:    <h1>Simple Grid with Custom Links</h1>
      19:    
      20:    <%=Html.GridView(ViewData.Model, Nothing, New GridViewOptions With {.ViewButtonText = "Look", .ShowEditButton = False, .ShowDeleteButton = False})%>
      21:  
      22:  
      23:  
      24: </asp:Content>

    Listing 2 – Index.aspx (C#)

       1: <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Tip8.Views.Home.Index" %>
       2: <%@ Import Namespace="Tip8.Helpers" %>
       3:  
       4: <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
       5:    
       6:    <h1>Simple Grid</h1>
       7:    
       8:    <%= Html.GridView(ViewData.Model) %>
       9:  
      10:    <h1>Simple Grid without Links</h1>
      11:    
      12:    <%= Html.GridView(ViewData.Model, false) %>
      13:  
      14:    <h1>Simple Grid with Custom Headers</h1>
      15:    
      16:    <%= Html.GridView(ViewData.Model, new string[] {"AA", "BB", "CC", "DD"} )%>
      17:    
      18:    <h1>Simple Grid with Custom Links</h1>
      19:    
      20:    <%= Html.GridView(ViewData.Model, null, new GridViewOptions { ViewButtonText = "Look", ShowEditButton=false, ShowDeleteButton=false } )%>
      21:  
      22:  
      23:    
      24: </asp:Content>

    The view in Listing 2 generates the HTML page displayed in Figure 1. The page contains four separate grids of data (the figure only shows the first three).

    Figure 1 – The Index View

    clip_image002

    Notice that the ViewData.Model is passed to the GridView() helper method. The ViewData.Model represents a LINQ to SQL Table. The code-behind file for the Index view strongly types the model as a System.Data.Linq.ITable class. The model is passed to the view by the controller code in Listing 3.

    Listing 3 – HomeController.vb (VB)

       1: Public Class HomeController
       2:     Inherits System.Web.Mvc.Controller
       3:  
       4:     Private _db As New MovieDataContext()
       5:  
       6:     Function Index()
       7:         Return View(_db.Movies)
       8:     End Function
       9: End Class

    Listing 3 – HomeController.cs (C#)

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Linq;
       4: using System.Web;
       5: using System.Web.Mvc;
       6: using Tip8.Models;
       7:  
       8: namespace Tip8.Controllers
       9: {
      10:     public class HomeController : Controller
      11:     {
      12:         private MovieDataContext _db = new MovieDataContext();
      13:  
      14:         public ActionResult Index()
      15:         {
      16:            return View(_db.Movies);
      17:         }
      18:     }
      19: }

    I’m not completely happy with the GridView() helper method discussed in this tip. The problem with using an extension method is that it makes it difficult to customize the appearance of the columns in the GridView. For example, I would like to be able to format currency and date columns. Better yet, it would be nice if there was a way to have the equivalent of a template column. In tomorrow’s tip, I will explore an entirely different method of encapsulating a GridView when working with the ASP.NET MVC framework.

    You can download the code for the GridView() helper method by clicking the following link. The download includes both Visual Basic .NET and C# versions of the code.

    Download the Code

  • ASP.NET MVC Tip #6 – Call RedirectToAction after Submitting a Form

    In this tip, I explain why you should call the RedirectToAction() method after submitting form data instead of returning a view.

    Imagine that you are collecting information from your website users with an HTML form. The HTML form is displayed by a controller action named HomeController.Create() and the form data is submitted to a controller action named HomeController.New() that adds the form data to the database. After the form data is submitted, you want to display the accumulated survey results (see Figure 1).

    Figure 1 – Results.aspx View

    clip_image002

    There are two approaches that you might take to writing the New() controller action. In Listing 1, the New() action first submits the form data to the database (using LINQ to SQL) and then calls RedirectToAction() to redirect the user to the Results() action. In Listing 2, the New() action does not call RedirectToAction(). Instead, the Results.aspx view is returned directly by the New() action.

    Listing 1 – SurveyController.vb

    Public Class SurveyController
        Inherits System.Web.Mvc.Controller
     
        Private _db As New SurveyDataContext()
     
        Function Create()
            Return View()
        End Function
     
        Function [New](ByVal favoriteColor As String)
     
            ' Add new survey results to database
            Dim newSurvey As New Survey()
            newSurvey.FavoriteColor = favoriteColor
            newSurvey.EntryDate = DateTime.Now
            _db.Surveys.InsertOnSubmit(newSurvey)
            _db.SubmitChanges()
     
            ' Redirect to Confirm action
            Return RedirectToAction("Results")
        End Function
     
        Function Results()
            Return View(_db.Surveys)
        End Function
     
    End Class

    Listing 2 – Survey2Controller.vb

    Public Class Survey2Controller
        Inherits System.Web.Mvc.Controller
     
        Private _db As New SurveyDataContext()
     
        Function Create()
            Return View()
        End Function
     
        Function [New](ByVal favoriteColor As String)
     
            ' Add new survey results to database
            Dim newSurvey As New Survey()
            newSurvey.FavoriteColor = favoriteColor
            newSurvey.EntryDate = DateTime.Now
            _db.Surveys.InsertOnSubmit(newSurvey)
            _db.SubmitChanges()
     
            ' Return Results view
            Return View("Results", _db.Surveys)
        End Function
     
    End Class

    So, there are two different approaches to showing a results page after submitting form data. You can either return RedirectToAction() or you can return View(). Which approach is better?

    When you call RedirectToAction(), the ASP.NET MVC framework causes the web browser to make a new request to your website. The RedirectToAction() method returns a 302 – Object Moved status to the browser. This browser fetches the Results view.

    So you might think calling RedirectToAction() is the poorer strategy. The browser has to do more work when you call RedirectToAction(). Something might go wrong on the network while the browser makes a new request. Using RedirectToAction() opens up more possibilities of things going wrong.

    However, there are three good reasons to prefer RedirectToAction() over returning a view directly: two reasons are practical and one reason is philosophical. Let’s start with the practical reasons. If you don’t do a redirect, and the user hits the refresh/reload button in the browser, the database data is submitted more than once. In other words, if you don’t do a redirect, you can get duplicate database data in your database tables.

    Now, it is true that modern browsers warn the user of this danger. Microsoft Internet Explorer 7.0 provides the nicely worded warning in Figure 2. So, perhaps this danger isn’t quite as bad as it was in the past.

    Figure 2 – IE warning when refreshing after form post

    clip_image004

    The second practical reason is related to the first. If you bookmark the results page (or email a link to the page to a friend) and use the bookmark to open the page later, the database operation will happen without warning. The form will be submitted without data, client-side validation will be bypassed, and you will get the ugly page in Figure 2:

    Figure 2 – Returning to Results bookmark

    clip_image006

    The third reason to prefer RedirectToAction() over View() is philosophical. The ASP.NET MVC framework provides a “RESTful” interface to your application. Different URLs correspond to different actions. If you return the Results view when the New() action is invoked, the correspondence between actions and views is broken. In other words, in a RESTful application, the view that you see should correspond to the URL that you see in your browser’s address bar.

    If you call RedirectToAction() after submitting the form data, you’ll see Survey/Results in your browser’s address bar. If you call View() after submitting the form data, you’ll see Survey/New in your browser’s address bar. Because you are viewing the Results page, the first scenario makes more sense. The browser address bar should reflect the state of the web application. Doing a redirect enables you to keep the browser and server in sync.

    If you would like to experiment with the two methods of submitting form data discussed in this tip, click the following link to download the code.

    Download the Code

    ********* UPDATE ****

    One nice thing about working at Microsoft is that you are surrounded by really smart people. After posting this blog entry, I ran into Brad Wilson (of xUnit fame) and he told me that there is a pattern that applies to the topic of this tip. The pattern is called the PRG pattern (Post-Redirect-Get pattern). Here's the link to the Wikipedia entry:

    http://en.wikipedia.org/wiki/Post/Redirect/Get

    So, this tip should really be renamed to "Use the PRG Pattern when Submitting a Form". 

  • ASP.NET MVC Tip #5 – Create Shared Views

    In this tip, I explain how to create shared views for controller actions. A shared view can be used with multiple controllers. In particular, I explain how to create shared views for displaying and editing database data.

    Imagine that the HomeController controller exposes an action named Index(). When the Index() action is invoked, the ASP.NET MVC framework first attempts to retrieve the Index view from the following path:

    \Views\Home\Index.aspx

    If the Index.aspx view is not present in the Home folder, the ASP.NET MVC framework next attempts to retrieve the view from the Shared folder:

    \Views\Shared\Index.aspx

    If the Index.aspx view can’t be retrieved from either location, then the error message in Figure 1 is displayed.

    Figure 1 – Error displayed when attempting to retrieve non-existent view

    clip_image002

    So, the ASP.NET MVC framework always attempts to retrieve a view from the Shared folder when the view can’t be retrieved from the Views subfolder that corresponds to the controller’s name. This means that you can create default views for action methods that are shared across multiple controllers.

    In today’s tip, I show you how to create shared views for performing standard database operations that can be used with the DataController class that we created in yesterday’s Tip #4. If you haven’t read yesterday’s tip, read it now at:

    http://stephenwalther.com/archive/2008/06/18/asp-net-mvc-tip-4-create-a-custom-data-controller-base-class.aspx

    I modified the DataController for this tip so that its controller actions return the LINQ to SQL DataContext to the view. For example, the modified DataController.Index() method looks like this:

    Public Function Index() As ActionResult
        ViewData("DataContext") = Me.DataContext
        ViewData("DataTable") = Me.Table
        Return View()
    End Function

    We need both the LINQ to SQL DataContext and LINQ to SQL Table to render the grid of records.

    The Index.aspx view, placed in the Views\Shared folder, is contained in Listing 1.

    Listing 1 – Views\Shared\Index.aspx

    <%@ Page Title="Index" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="Tip5.SharedViews.Index" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
     
    <%=Me.RenderDataTable()%>
    <br />
    <%= Html.ActionLink("Add Record", "Create") %>
     
    </asp:Content>

    The Index.aspx view in Listing 1 simply calls the RenderDataTable() method declared in the view’s code-behind file. The Index.aspx.vb code, the code-behind file for Index.aspx, is contained in Listing 2.

    Listing 2 -- \Views\Shared\Index.aspx.vb

    Namespace SharedViews
     
        Partial Public Class Index
            Inherits DataViewBase
     
     
            Protected Function RenderDataTable() As String
                Dim sb As New StringBuilder()
                sb.AppendLine("<table>")
     
                ' Create Header Row
                Dim columnNames As String() = Me.GetColumnNames()
                sb.AppendLine("<tr>")
                sb.Append("<th></th>")
                For Each columnName As String In columnNames
                    sb.AppendFormat("<th>{0}</th>", columnName)
                Next
                sb.AppendLine("</tr>")
     
                ' Create Data Rows
                Dim row As Object
                For Each row In Me.DataTable
                    Dim identityValue As Integer = Me.GetIdentityValue(row)
                    sb.AppendLine("<tr>")
                    sb.Append("<td><small>")
                    sb.Append(Me.Html.ActionLink("[View]", "Details", New With {.Id = identityValue}))
                    sb.Append("&nbsp;")
                    sb.Append(Me.Html.ActionLink("[Edit]", "Edit", New With {.Id = identityValue}))
                    sb.Append("&nbsp;")
                    sb.Append(Me.Html.ActionLink("[Delete]", "Delete", New With {.Id = identityValue}))
                    sb.Append("</small></td>")
                    For Each columnName As String In columnNames
                        Dim value As String = row.GetType() _
                            .GetProperty(columnName) _
                            .GetValue(row, Nothing) _
                            .ToString()
                        sb.AppendFormat("<td>{0}</td>", HttpUtility.HtmlEncode(value))
                    Next
                    sb.AppendLine("</tr>")
                Next
     
                sb.AppendLine("</table>")
                Return sb.ToString()
            End Function
     
        End Class
    End Namespace

    The RenderDataTable() method generates an HTML table that contains all of the columns and all of the rows represented by the DataTable property. The DataTable is passed by the DataController.Index() controller action to the Index.aspx view.

    When you request the Index.aspx view, you get the page displayed in Figure 2.

    Figure 2 – The Index.aspx view

    clip_image004

    Notice that View, Edit, and Delete links appear next to each record. You can click these links to perform the corresponding actions. Notice, furthermore, that there is an Add Record link at the bottom of the page. Clicking this link opens the Create.aspx view in Figure 3.

    Figure 3 – The Create.aspx View

    clip_image006

    The form generated by the Create.aspx view is not pretty, and it does not support validation, but otherwise it works. If you enter a new movie, you can submit the new movie to the database.

    The Create form is generated with the code in Listing 3.

    Listing 3 – Views\Shared\Create.aspx.vb

    Namespace SharedViews
     
        Partial Public Class Create
            Inherits DataViewBase
     
     
     
            Protected Function RenderCreateForm() As String
                Dim sb As New StringBuilder()
                Dim columnNames = Me.GetColumnNames()
                Dim identityColumnName = Me.GetIdentityColumnName()
     
                sb.AppendFormat("<form method='post' action='{0}'>", Me.Url.Action("New"))
                sb.AppendLine("<table>")
                Dim columnName As String
                For Each columnName In columnNames
                    If columnName <> identityColumnName Then
                        sb.AppendLine("<tr>")
                        sb.AppendFormat("<th><label for='{0}'>{0}:</label></td>", columnName)
                        sb.AppendFormat("<td><input name='{0}' type='text' /></td>", columnName)
                        sb.AppendLine("</tr>")
                    End If
                Next
                sb.AppendLine("</table>")
                sb.AppendLine("<input type='submit' value='Add Record' />")
                sb.AppendLine("</form>")
     
                Return sb.ToString()
            End Function
     
        End Class
    End Namespace

    The code in Listing 3 uses the LINQ to SQL DataContext and LINQ to SQL Table to generate the form automatically. Both of these objects are passed by the DataController.Create() controller action.

    The purpose of this tip was to give you a sense of what you can do with shared views. In this tip, we examined how you could use shared views to automatically generate display, edit, and insert forms for working with database data. You could follow a similar strategy to create standard views for other types of controller actions (for example, standard shopping cart views).

    If you would like to experiment with the code discussed in this tip, click the following link (The download includes both VB.NET and C# versions of the code).

    Download the Code

  • ASP.NET MVC Tip #4 - Create a Custom Data Controller Base Class

    In this tip, you learn how to create a custom controller base class that exposes actions for performing common database operations such as displaying, inserting, updating, and deleting data.

    Whenever you write code and you discover that you are writing the same type of code over and over again, that is a good time to stop and consider whether you are wasting huge amounts of time. Yesterday, I discovered that I was in this very situation while building a database-driven ASP.NET MVC web application. I needed to perform the same standard set of database operations – display data, update data, insert data, delete data – for each of the database tables in my application. The dreadful prospect of having to write the exact same code over and over again inspired me to write today’s ASP.NET MVC tip of the day.

    An MVC controller is just a class (a Visual Basic or C# class). Classes support inheritance. So, if you find yourself writing the exact same logic for your controller actions, it makes sense to write a new base class that contains the common set of actions. In this tip, we are going to create a base controller class that performs standard database operations. Keep in mind that you can create base controller classes for other types of common controller actions.

    I created a base controller class named the DataController class. This class supports the following public methods:

    • Index() – Displays all of the database records in a database table
    • Details() – Displays a single database record from a database table
    • Edit() – Displays a view that contains a form for editing an existing database record
    • Update() – Updates a database record in the database
    • New() – Displays a view for inserting a new database record
    • Insert() – Inserts a new database record into the database
    • Delete() – Deletes an existing record from the database

    Since each of these methods is public, each of these methods is exposed as an action. You can invoke any of these methods by typing the right URL in your web browser’s address bar.

    The DataController class also supports several protected methods:

    • DBSave() – Either inserts or updates a database record
    • DBInsert() – Inserts a new database record
    • DBUpdate() – Updates an existing database record
    • DBDelete() – Deletes an existing database record
    • DBGet() – Gets a single database record

    Since these methods are protected, they cannot be invoked through a URL. However, you can call any of these methods within your derived controller class. These are useful utility methods that you can call from a derived controller’s action methods.

    Finally, the DataController class supports the following properties:

    • DataContext – The LINQ to SQL data context.
    • Table – The LINQ to SQL Table.
    • IdentityColumnName – The name of the identity column contained in the database table.

    These properties are also protected. You can use them from within your derived controller class, but they are not exposed as controller actions.

    The DataController is a generic class. When you create a controller that derives from the DataController class, you must specify the type of database entity that the DataController class represents. The DataController class works with LINQ to SQL. Before you use the DataController class, you must first create your LINQ to SQL entities that represent your database objects.

    For example, Listing 1 contains a HomeController class that derives from the DataController class. Notice that the Movie type is passed to the DataController class. The Movie class is a LINQ to SQL entity created with the Visual Studio Object Relational Designer.

    Listing1 – HomeController.vb (VB)

       1: Imports System
       2: Imports System.Collections.Generic
       3: Imports System.Linq
       4: Imports System.Web
       5: Imports System.Web.Mvc
       6:  
       7:  
       8: Namespace Tip4.Controllers
       9:     Public Class HomeController
      10:         Inherits DataController(Of Movie)
      11:  
      12:         ''' <summary>
      13:         ''' Show Movies in a Category
      14:         ''' </summary>
      15:         Public Function Category(ByVal Id As Integer) As ActionResult
      16:             Dim results = From m In Me.Table Where m.CategoryId = Id Select m
      17:             Return View(results)
      18:         End Function
      19:  
      20:     End Class
      21: End Namespace
      22:  

    Listing1 – HomeController.vb (CS)

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Linq;
       4: using System.Web;
       5: using System.Web.Mvc;
       6:  
       7: using Tip4.Models;
       8:  
       9: namespace Tip4.Controllers
      10: {
      11:     public class HomeController : DataController<Movie>
      12:     {
      13:  
      14:         /// <summary>
      15:         /// Show Movies in a Category
      16:         /// </summary>
      17:         public ActionResult Category(int Id)
      18:         {
      19:             var results = from m in this.Table where m.CategoryId == Id select m;
      20:             return View(results);
      21:         }
      22:  
      23:  
      24:     }
      25: }

    Because the HomeController class derives from the DataController class, the HomeController class exposes Index(), Details(), Create(), New(), Edit(), Update(), and Delete() actions automatically. Because the Movie entity is passed to the DataController, the HomeController enables you to perform these actions against the Movies database table.

    Before you use the DataController class, you must add a connection string named dataController to your application’s web.config file. You can copy the connection string generated by the Visual Studio Object Relational Designer and rename the connection string dataController.

    You must still create a set of views to use the DataController class. You need to create the following set of views:

    • Index.aspx
    • Details.aspx
    • Create.aspx
    • Edit.aspx

    In tomorrow's tip, I'll show you how to create these views just once for all of your controller classes by creating Shared Views. But, that is tomorrow's topic. Back to the subject of the DataController.

    Unfortunately, the code for the DataController class is too long to paste into this blog entry. You can download the DataController, and try it out by experimenting with the sample project, by clicking the Download the Code link at the end of this blog entry.

    The sample project contains the four views listed above. You can use the sample project to display, insert, update, and delete records from the Movies database table. For example, Figure 1 contains the page generated by the Index.aspx view.

    Figure 1 –The Index.aspx View

    clip_image002

    My expectation and hope is that there will be hundreds of custom base controller classes created by developers actively working with the ASP.NET MVC framework when the framework has its final release. I can imagine base controller classes used in a number of different scenarios: authentication, shopping carts, product catalogs, and so on. Anytime that you need to include a standard set of actions in more than one application, it makes sense to create a new controller base class.

    Download the Code

  • ASP.NET MVC Tip #3 – Provide Explicit View Names when Unit Testing

    In this tip, Stephen Walther explains how you can unit test whether a controller action returns a particular view. He recommends that you be explicit about view names when you plan to create unit tests.

    The ASP.NET MVC framework was designed to be a very testable framework. You can easily test an MVC controller action to determine whether the action returns the result that you expect. In this tip, I show you how to test whether a controller action returns a particular view.

    Consider the MVC controller, named HomeController, in Listing 1. This controller contains an action named Index(). The Index() action returns a view. However, the name of the view is not provided. Instead, the name of the view is inferred from the name of the controller action. Therefore, when you call the Index() action, a view named Index is returned.

    The HomeController contains a second action named Index2(). This second action also returns a view. However, in the second action, the name of the view is explicit. The name of the view is passed to the View() method. This second controller action does the same thing as the first controller action. However, in the case of the first controller action the view name is inferred and in the case of the second controller action the view name is explicit.

    Listing 1 - HomeController.vb (VB)

       1: Public Class HomeController
       2:     Inherits System.Web.Mvc.Controller
       3:  
       4:     Function Index() As ActionResult
       5:         Return View() ' view name inferred
       6:     End Function
       7:  
       8:     Function Index2() As ActionResult
       9:         Return View("Index") ' view name explicit
      10:     End Function
      11:  
      12: End Class

    Listing 1 - HomeController.cs (C#)

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Linq;
       4: using System.Web;
       5: using System.Web.Mvc;
       6:  
       7: namespace Tip3.Controllers
       8: {
       9:     public class HomeController : Controller
      10:     {
      11:         public ActionResult Index()
      12:         {
      13:             return View(); // view name inferred
      14:         }
      15:  
      16:         public ActionResult Index2()
      17:         {
      18:             return View("Index"); // view name explicit
      19:         }
      20:  
      21:     
      22:     }
      23: }

    If you plan to create unit tests for your ASP.NET MVC application, then you should always be explicit about your view names. Otherwise, you won’t be able to test whether the right view was returned in your unit tests.

    The test class in Listing 2 contains two test methods. The first method tests the HomeController’s Index() action and the second method tests the HomeController’s Index2() action. The first test always fails and the second test always succeeds (see Figure 1).

    Listing 2 - HomeControllerTest.vb (VB)

       1: Imports System
       2: Imports System.Collections.Generic
       3: Imports System.Text
       4: Imports System.Web.Mvc
       5: Imports Microsoft.VisualStudio.TestTools.UnitTesting
       6: Imports Tip3
       7:  
       8: <TestClass()> Public Class HomeControllerTest
       9:  
      10:  
      11:     <TestMethod()> _
      12:   Public Sub Index()
      13:         ' Arrange
      14:         Dim controller As New HomeController()
      15:  
      16:         ' Act
      17:         Dim result As ViewResult = controller.Index()
      18:  
      19:         ' Assert
      20:         Assert.AreEqual("Index", result.ViewName)
      21:     End Sub
      22:  
      23:     <TestMethod()> _
      24:   Public Sub Index2()
      25:         ' Arrange
      26:         Dim controller As New HomeController()
      27:  
      28:         ' Act
      29:         Dim result As ViewResult = controller.Index2()
      30:  
      31:         ' Assert
      32:         Assert.AreEqual("Index", result.ViewName)
      33:     End Sub
      34:  
      35:  
      36:  
      37: End Class

    Listing 2 - HomeControllerTest.cs (C#)

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Linq;
       4: using System.Text;
       5: using System.Web.Mvc;
       6: using Microsoft.VisualStudio.TestTools.UnitTesting;
       7: using Tip3;
       8: using Tip3.Controllers;
       9:  
      10: namespace Tip3Tests.Controllers
      11: {
      12:     /// <summary>
      13:     /// Summary description for HomeControllerTest
      14:     /// </summary>
      15:     [TestClass]
      16:     public class HomeControllerTest
      17:     {
      18:         [TestMethod]
      19:         public void Index()
      20:         {
      21:             // Arrange
      22:             HomeController controller = new HomeController();
      23:  
      24:             // Act
      25:             ViewResult result = controller.Index() as ViewResult;
      26:  
      27:             // Assert
      28:             Assert.AreEqual("Index", result.ViewName);
      29:         }
      30:  
      31:  
      32:         [TestMethod]
      33:         public void Index2()
      34:         {
      35:             // Arrange
      36:             HomeController controller = new HomeController();
      37:  
      38:             // Act
      39:             ViewResult result = controller.Index2() as ViewResult;
      40:  
      41:             // Assert
      42:             Assert.AreEqual("Index", result.ViewName);
      43:         }
      44:  
      45:  
      46:     }
      47: }

    Figure 1 – Unit Test Results

    image

    A unit test cannot infer a view name. My recommendation is that you should always be explicit about your view names if you plan to unit test your application.

    Download the Code

  • ASP.NET MVC Tip #2 - Create a custom Action Result that returns Microsoft Excel Documents

    In this tip, I show you how to create a custom action result that you can return from an ASP.NET MVC controller action. This action result generates a Microsoft Excel Document from a LINQ to SQL query.

    In an MVC application, a controller action returns an action result. In particular, it returns something that derives from the base ActionResult class such as:

    · ViewResult

    · EmptyResult

    · RedirectResult

    · RedirectToRouteResult

    · JsonResult

    · ContentResult

    For example, you use a ViewResult to return a particular view to the browser and a ContentResult to return text content to the browser.

    But, what if you want to return some other type of content to a browser such as an image, a PDF file, or a Microsoft Excel document? In these cases, you can create your own action result. In this tip, I show you how to create an action result that returns a Microsoft Excel document.

    The code for the ExcelResult is contained in Listing 1.

    Listing 1 – ExcelResult.vb (VB)

       1: Imports System
       2: Imports System.Web.Mvc
       3: Imports System.Data.Linq
       4: Imports System.Collections
       5: Imports System.IO
       6: Imports System.Web.UI.WebControls
       7: Imports System.Linq
       8: Imports System.Web
       9: Imports System.Web.UI
      10: Imports System.Drawing
      11:  
      12:  
      13: Namespace Tip2
      14:  
      15:     Public Class ExcelResult
      16:         Inherits ActionResult
      17:  
      18:         Private _dataContext As DataContext
      19:         Private _fileName As String
      20:         Private _rows As IQueryable
      21:         Private _headers() As String = Nothing
      22:  
      23:         Private _tableStyle As TableStyle
      24:         Private _headerStyle As TableItemStyle
      25:         Private _itemStyle As TableItemStyle
      26:  
      27:         Public ReadOnly Property FileName() As String
      28:             Get
      29:                 Return _fileName
      30:             End Get
      31:         End Property
      32:  
      33:         Public ReadOnly Property Rows() As IQueryable
      34:             Get
      35:                 Return _rows
      36:             End Get
      37:         End Property
      38:  
      39:  
      40:         Public Sub New(ByVal dataContext As DataContext, ByVal rows As IQueryable, ByVal fileName As String)
      41:             Me.New(dataContext, rows, fileName, Nothing, Nothing, Nothing, Nothing)
      42:         End Sub
      43:  
      44:         Public Sub New(ByVal dataContext As DataContext, ByVal fileName As String, ByVal rows As IQueryable, ByVal headers() As String)
      45:             Me.New(dataContext, rows, fileName, headers, Nothing, Nothing, Nothing)
      46:         End Sub
      47:  
      48:         Public Sub New(ByVal dataContext As DataContext, ByVal rows As IQueryable, ByVal fileName As String, ByVal headers() As String, ByVal tableStyle As TableStyle, ByVal headerStyle As TableItemStyle, ByVal itemStyle As TableItemStyle)
      49:             _dataContext = dataContext
      50:             _rows = rows
      51:             _fileName = fileName
      52:             _headers = headers
      53:             _tableStyle = tableStyle
      54:             _headerStyle = headerStyle
      55:             _itemStyle = itemStyle
      56:  
      57:             ' provide defaults
      58:             If _tableStyle Is Nothing Then
      59:                 _tableStyle = New TableStyle()
      60:                 _tableStyle.BorderStyle = BorderStyle.Solid
      61:                 _tableStyle.BorderColor = Color.Black
      62:                 _tableStyle.BorderWidth = Unit.Parse("2px")
      63:             End If
      64:             If _headerStyle Is Nothing Then
      65:                 _headerStyle = New TableItemStyle()
      66:                 _headerStyle.BackColor = Color.LightGray
      67:             End If
      68:         End Sub
      69:  
      70:         Public Overrides Sub ExecuteResult(ByVal context As ControllerContext)
      71:             ' Create HtmlTextWriter
      72:             Dim sw As StringWriter = New StringWriter()
      73:             Dim tw As HtmlTextWriter = New HtmlTextWriter(sw)
      74:  
      75:             ' Build HTML Table from Items
      76:             If Not _tableStyle Is Nothing Then
      77:                 _tableStyle.AddAttributesToRender(tw)
      78:             End If
      79:             tw.RenderBeginTag(HtmlTextWriterTag.Table)
      80:  
      81:             ' Generate headers from table
      82:             If _headers Is Nothing Then
      83:                 _headers = _dataContext.Mapping.GetMetaType(_rows.ElementType).PersistentDataMembers.Select(Function(m) m.Name).ToArray()
      84:             End If
      85:  
      86:  
      87:             ' Create Header Row
      88:             tw.RenderBeginTag(HtmlTextWriterTag.Thead)
      89:             For Each header As String In _headers
      90:                 If Not _headerStyle Is Nothing Then
      91:                     _headerStyle.AddAttributesToRender(tw)
      92:                 End If
      93:                 tw.RenderBeginTag(HtmlTextWriterTag.Th)
      94:                 tw.Write(header)
      95:                 tw.RenderEndTag()
      96:             Next
      97:             tw.RenderEndTag()
      98:  
      99:  
     100:  
     101:             ' Create Data Rows
     102:             tw.RenderBeginTag(HtmlTextWriterTag.Tbody)
     103:             For Each row As Object In _rows
     104:                 tw.RenderBeginTag(HtmlTextWriterTag.Tr)
     105:                 Dim header As String
     106:                 For Each header In _headers
     107:                     Dim strValue As String = row.GetType().GetProperty(header).GetValue(row, Nothing).ToString()
     108:                     If Not _itemStyle Is Nothing Then
     109:                         _itemStyle.AddAttributesToRender(tw)
     110:                     End If
     111:                     tw.RenderBeginTag(HtmlTextWriterTag.Td)
     112:                     tw.Write(HttpUtility.HtmlEncode(strValue))
     113:                     tw.RenderEndTag()
     114:                 Next
     115:                 tw.RenderEndTag()
     116:             Next
     117:             tw.RenderEndTag() ' tbody
     118:  
     119:             tw.RenderEndTag() ' table
     120:             WriteFile(_fileName, "application/ms-excel", sw.ToString())
     121:         End Sub
     122:  
     123:  
     124:       
     125:  
     126:         Private Shared Sub WriteFile(ByVal fileName As String, ByVal contentType As String, ByVal content As String)
     127:             Dim context As HttpContext = HttpContext.Current
     128:             context.Response.Clear()
     129:             context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName)
     130:             context.Response.Charset = ""
     131:             context.Response.Cache.SetCacheability(HttpCacheability.NoCache)
     132:             context.Response.ContentType = contentType
     133:             context.Response.Write(content)
     134:             context.Response.End()
     135:         End Sub
     136:     End Class
     137: End Namespace
     138:  

    Listing 1 – ExcelResult.cs (C#)

       1: using System;
       2: using System.Web.Mvc;
       3: using System.Data.Linq;
       4: using System.Collections;
       5: using System.IO;
       6: using System.Web.UI.WebControls;
       7: using System.Linq;
       8: using System.Web;
       9: using System.Web.UI;
      10: using System.Drawing;
      11:  
      12:  
      13: namespace Tip2
      14: {
      15:     public class ExcelResult : ActionResult
      16:     {
      17:         private DataContext _dataContext;
      18:         private string _fileName;
      19:         private IQueryable _rows;
      20:         private string[] _headers = null;
      21:  
      22:         private TableStyle _tableStyle;
      23:         private TableItemStyle _headerStyle;
      24:         private TableItemStyle _itemStyle;
      25:  
      26:         public string FileName
      27:         {
      28:             get { return _fileName; }
      29:         }
      30:  
      31:         public IQueryable Rows
      32:         {
      33:             get { return _rows; }
      34:         }
      35:  
      36:  
      37:         public ExcelResult(DataContext dataContext, IQueryable rows, string fileName)
      38:             :this(dataContext, rows, fileName, null, null, null, null)
      39:         {
      40:         }
      41:  
      42:         public ExcelResult(DataContext dataContext, string fileName, IQueryable rows, string[] headers)
      43:             : this(dataContext, rows, fileName, headers, null, null, null)
      44:         {
      45:         }
      46:  
      47:         public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers, TableStyle tableStyle, TableItemStyle headerStyle, TableItemStyle itemStyle)
      48:         {
      49:             _dataContext = dataContext;
      50:             _rows = rows;
      51:             _fileName = fileName;
      52:             _headers = headers;
      53:             _tableStyle = tableStyle;
      54:             _headerStyle = headerStyle;
      55:             _itemStyle = itemStyle;
      56:  
      57:             // provide defaults
      58:             if (_tableStyle == null)
      59:             {
      60:                 _tableStyle = new TableStyle();
      61:                 _tableStyle.BorderStyle = BorderStyle.Solid;
      62:                 _tableStyle.BorderColor = Color.Black;
      63:                 _tableStyle.BorderWidth = Unit.Parse("2px");
      64:             }
      65:             if (_headerStyle == null)
      66:             {
      67:                 _headerStyle = new TableItemStyle();
      68:                 _headerStyle.BackColor = Color.LightGray;
      69:             }
      70:         }
      71:  
      72:         public override void ExecuteResult(ControllerContext context)
      73:         {
      74:             // Create HtmlTextWriter
      75:             StringWriter sw = new StringWriter();
      76:             HtmlTextWriter tw = new HtmlTextWriter(sw);
      77:  
      78:             // Build HTML Table from Items
      79:             if (_tableStyle != null)
      80:                 _tableStyle.AddAttributesToRender(tw);
      81:             tw.RenderBeginTag(HtmlTextWriterTag.Table);
      82:  
      83:             // Generate headers from table
      84:             if (_headers == null)
      85:             {
      86:                 _headers = _dataContext.Mapping.GetMetaType(_rows.ElementType).PersistentDataMembers.Select(m => m.Name).ToArray();
      87:             }
      88:  
      89:  
      90:             // Create Header Row
      91:             tw.RenderBeginTag(HtmlTextWriterTag.Thead);
      92:             foreach (String header in _headers)
      93:             {
      94:                 if (_headerStyle != null)
      95:                     _headerStyle.AddAttributesToRender(tw);
      96:                 tw.RenderBeginTag(HtmlTextWriterTag.Th);
      97:                 tw.Write(header);
      98:                 tw.RenderEndTag();
      99:             }
     100:             tw.RenderEndTag();
     101:  
     102:             
     103:  
     104:             // Create Data Rows
     105:             tw.RenderBeginTag(HtmlTextWriterTag.Tbody);
     106:             foreach (Object row in _rows)
     107:             {
     108:                 tw.RenderBeginTag(HtmlTextWriterTag.Tr);
     109:                 foreach (string header in _headers)
     110:                 {
     111:                     string strValue = row.GetType().GetProperty(header).GetValue(row, null).ToString();
     112:                     strValue = ReplaceSpecialCharacters(strValue);
     113:                     if (_itemStyle != null)
     114:                         _itemStyle.AddAttributesToRender(tw);
     115:                     tw.RenderBeginTag(HtmlTextWriterTag.Td);
     116:                     tw.Write( HttpUtility.HtmlEncode(strValue));
     117:                     tw.RenderEndTag();
     118:                 }
     119:                 tw.RenderEndTag();
     120:             }
     121:             tw.RenderEndTag(); // tbody
     122:  
     123:             tw.RenderEndTag(); // table
     124:             WriteFile(_fileName, "application/ms-excel", sw.ToString());            
     125:         }
     126:  
     127:  
     128:         private static string ReplaceSpecialCharacters(string value)
     129:         {
     130:             value = value.Replace("’", "'");
     131:             value = value.Replace("“", "\"");
     132:             value = value.Replace("”", "\"");
     133:             value = value.Replace("–", "-");
     134:             value = value.Replace("…", "...");
     135:             return value;
     136:         }
     137:  
     138:         private static void WriteFile(string fileName, string contentType, string content)
     139:         {
     140:             HttpContext context = HttpContext.Current;
     141:             context.Response.Clear();
     142:             context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
     143:             context.Response.Charset = "";
     144:             context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
     145:             context.Response.ContentType = contentType;
     146:             context.Response.Write(content);
     147:             context.Response.End();
     148:         }
     149:     }
     150: }

    Every action result must inherit from the base ActionResult class. The ExcelResult class in Listing 1 does, in fact, inherit from the base ActionResult class. The base ActionResult class has one method that you must implement: the Execute() method. The Execute() method is called to generate the content created by the action result.

    In Listing 1, the Execute() method is used to generate the Excel document from a Linq to SQL query. The Execute() method calls the WriteFile() method to write the finished Excel document to the browser with the correct MIME type.

    Normally, you do not return an action result from a controller action directly. Instead, you take advantage of one of the methods of the Controller class:

    · View()

    · Redirect()

    · RedirectToAction()

    · RedirectToRoute()

    · Json()

    · Content()

    For example, if you want to return a view from a controller action, you don’t return a ViewResult. Instead, you call the View() method. The View() method instantiates a new ViewResult and returns the new ViewResult to the browser.

    The code in Listing 2 consists of three extension methods that are applied to the Controller class. These extension methods add a new method named Excel() to the Controller class. The Excel() method returns an ExcelResult().

    Listing 2 –ExcelControllerExtensions.vb (VB)

       1: Imports System
       2: Imports System.Web.Mvc
       3: Imports System.Data.Linq
       4: Imports System.Collections
       5: Imports System.Web.UI.WebControls
       6: Imports System.Linq
       7: Imports System.Runtime.CompilerServices
       8:  
       9: Namespace Tip2
      10:     Public Module ExcelControllerExtensions
      11:  
      12:         <Extension()> _
      13:         Function Excel(ByVal controller As Controller, ByVal dataContext As DataContext, ByVal rows As IQueryable, ByVal fileName As String) As ActionResult
      14:             Return New ExcelResult(DataContext, rows, fileName, Nothing, Nothing, Nothing, Nothing)
      15:         End Function
      16:  
      17:         <Extension()> _
      18:         Function Excel(ByVal controller As Controller, ByVal dataContext As DataContext, ByVal rows As IQueryable, ByVal fileName As String, ByVal headers As String()) As ActionResult
      19:             Return New ExcelResult(dataContext, rows, fileName, headers, Nothing, Nothing, Nothing)
      20:         End Function
      21:  
      22:         <Extension()> _
      23:         Function Excel(ByVal controller As Controller, ByVal dataContext As DataContext, ByVal rows As IQueryable, ByVal fileName As String, ByVal headers As String(), ByVal tableStyle As TableStyle, ByVal headerStyle As TableItemStyle, ByVal itemStyle As TableItemStyle) As ActionResult
      24:             Return New ExcelResult(dataContext, rows, fileName, headers, tableStyle, headerStyle, itemStyle)
      25:         End Function
      26:  
      27:     End Module
      28: End Namespace
      29:  

    Listing 2 –ExcelControllerExtensions.cs (C#)

       1: using System;
       2: using System.Web.Mvc;
       3: using System.Data.Linq;
       4: using System.Collections;
       5: using System.Web.UI.WebControls;
       6: using System.Linq;
       7:  
       8: namespace Tip2
       9: {
      10:     public static class ExcelControllerExtensions
      11:     {
      12:  
      13:         public static ActionResult Excel
      14:         (
      15:             this Controller controller,
      16:             DataContext dataContext,
      17:             IQueryable rows,
      18:             string fileName
      19:         )
      20:         {
      21:             return new ExcelResult(dataContext, rows, fileName, null, null, null, null);
      22:         }
      23:  
      24:         public static ActionResult Excel
      25:         (
      26:             this Controller controller,
      27:             DataContext dataContext,
      28:             IQueryable rows,
      29:             string fileName,
      30:             string[] headers
      31:         )
      32:         {
      33:             return new ExcelResult(dataContext, rows, fileName, headers, null, null, null);
      34:         }
      35:  
      36:         public static ActionResult Excel
      37:         (
      38:             this Controller controller, 
      39:             DataContext dataContext,
      40:             IQueryable rows, 
      41:             string fileName, 
      42:             string[] headers, 
      43:             TableStyle tableStyle, 
      44:             TableItemStyle headerStyle,
      45:             TableItemStyle itemStyle
      46:         )
      47:         {
      48:             return new ExcelResult(dataContext, rows, fileName, headers, tableStyle, headerStyle, itemStyle);
      49:         }
      50:  
      51:     }
      52: }

    The controller in Listing 3 illustrates how you can use the Excel() extension method within a controller. This controller includes three methods named GenerateExcel1(), GenerateExcel2(), and GenerateExcel3(). All three of the controller action methods return an Excel document by generating the document from the Movies database table.

    Listing 3 – HomeController.vb (VB)

       1: Imports System
       2: Imports System.Collections.Generic
       3: Imports System.Linq
       4: Imports System.Data.Linq
       5: Imports System.Data.Linq.Mapping
       6: Imports System.Web.UI.WebControls
       7: Imports System.Web
       8: Imports System.Web.Mvc
       9: Imports Tip2
      10:  
      11: Namespace Tip2.Controllers
      12:     Public Class HomeController
      13:         Inherits Controller
      14:  
      15:         Private db As New MovieDataContext()
      16:  
      17:         Public Function Index() As ActionResult
      18:             Return View()
      19:         End Function
      20:  
      21:         ''' <summary>
      22:         ''' Generates Excel document using headers grabbed from property names
      23:         ''' </summary>
      24:         Public Function GenerateExcel1() As ActionResult
      25:             Return Me.Excel(db, db.Movies, "data.xls")
      26:         End Function
      27:  
      28:         ''' <summary>
      29:         ''' Generates Excel document using supplied headers
      30:         ''' </summary>
      31:         Public Function GenerateExcel2() As ActionResult
      32:             Dim rows = From m In db.Movies Select New With {.Title = m.Title, .Director = m.Director}
      33:  
      34:             Return Me.Excel(db, rows, "data.xls", New String() {"Title", "Director"})
      35:         End Function
      36:  
      37:  
      38:         ''' <summary>
      39:         ''' Generates Excel document using supplied headers and using supplied styles
      40:         ''' </summary>
      41:         Public Function GenerateExcel3() As ActionResult
      42:             Dim rows = From m In db.Movies Select New With {.Title = m.Title, .Director = m.Director}
      43:  
      44:             Dim headerStyle As New TableItemStyle()
      45:             headerStyle.BackColor = System.Drawing.Color.Orange
      46:             Return Me.Excel(db, rows, "data.xls", New String() {"Title", "Director"}, Nothing, headerStyle, Nothing)
      47:         End Function
      48:  
      49:  
      50:     End Class
      51: End Namespace

    Listing 3 – HomeController.cs (C#)

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Linq;
       4: using System.Data.Linq;
       5: using System.Data.Linq.Mapping;
       6: using System.Web.UI.WebControls;
       7: using System.Web;
       8: using System.Web.Mvc;
       9: using Tip2.Models;
      10: using Tip2;
      11:  
      12: namespace Tip2.Controllers
      13: {
      14:     public class HomeController : Controller
      15:     {
      16:  
      17:         private MovieDataContext db = new MovieDataContext();
      18:  
      19:         public ActionResult Index()
      20:         {
      21:             return View();
      22:         }
      23:  
      24:         /// <summary>
      25:         /// Generates Excel document using headers grabbed from property names
      26:         /// </summary>
      27:         public ActionResult GenerateExcel1()
      28:         {
      29:             return this.Excel(db, db.Movies, "data.xls");            
      30:         }
      31:  
      32:         /// <summary>
      33:         /// Generates Excel document using supplied headers
      34:         /// </summary>
      35:         public ActionResult GenerateExcel2()
      36:         {
      37:             var rows = from m in db.Movies select new {Title=m.Title, Director=m.Director};
      38:             return this.Excel(db, rows, "data.xls", new[] { "Title", "Director" });
      39:         }
      40:  
      41:         /// <summary>
      42:         /// Generates Excel document using supplied headers and using supplied styles
      43:         /// </summary>
      44:         public ActionResult GenerateExcel3()
      45:         {
      46:             var rows = from m in db.Movies select new { Title = m.Title, Director = m.Director };
      47:             var headerStyle = new TableItemStyle();
      48:             headerStyle.BackColor = System.Drawing.Color.Orange;
      49:             return this.Excel(db, rows, "data.xls", new[] { "Title", "Director" }, null, headerStyle, null);
      50:         }
      51:     
      52:  
      53:     }
      54: }

    Finally, the Index.aspx view in Listing 4 demonstrates how you can call the GenerateExcel() controller actions to generate the Excel documents. Notice the three links to the three different versions of GenerateExcel.

    Listing 4 – Index.aspx

       1: <%@ Page Language="VB" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="Tip2.Index" %>
       2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
       3:  
       4: <html xmlns="http://www.w3.org/1999/xhtml" >
       5: <head id="Head1" runat="server">
       6:     <title>Index Page</title>
       7:     <style type="text/css">
       8:     
       9:     li
      10:     {
      11:         margin-bottom: 5px;
      12:     }
      13:     
      14:     </style>
      15: </head>
      16: <body>
      17:     <div>
      18:     
      19:     <h1>Generate Microsoft Excel Document</h1>
      20:     
      21:     
      22:     <ul>
      23:         <li>
      24:         <a href="/Home/GenerateExcel1">Generate</a> - Generates an Excel document by using the entity property names for column headings and the default
      25:         formatting.        
      26:         </li>
      27:         <li>
      28:         <a href="/Home/GenerateExcel2">Generate</a> - Generates an Excel document by using supplied header names and default formatting.        
      29:         </li>
      30:         <li>
      31:         <a href="/Home/GenerateExcel3">Generate</a> - Generates an Excel document by using supplied header names and supplied formatting.        
      32:         </li>
      33:  
      34:     </ul>
      35:     
      36:     
      37:     
      38:     
      39:     </div>
      40: </body>
      41: </html>

    When you open the Index view, you see the page in Figure 1.

    Figure 1 – The Index.aspx View

    image

    When you click one of the Generate Excel links, you get different Excel documents. For example, after you click on the first link, you get the Excel document in Figure 2.

    Figure 2 – Data.xls

    image

    One disappointing note. When you click a link to generate the Excel document, you receive the warning in Figure 3. Unfortunately, there is no way around displaying this warning (to learn more about this warning, see http://blogs.msdn.com/vsofficedeveloper/pages/Excel-2007-Extension-Warning.aspx).

    Figure 3 - Warning from Microsoft Internet Explorer

    image

    You can follow the same approach discussed in this tip to create other types of action results. For example, you can create image action results, Microsoft Word action results, or PDF action results.

    Click Here to Download the Code

  • ASP.NET MVC Tip #1 - Create New HTML Helpers with Extension Methods

    In this tip, I show you how you can create two new HTML Helpers that you can use within an ASP.NET MVC View. I show you how you can use extension methods to create new HTML Helpers for displaying bulleted and numbered lists.

    When building a View for an ASP.NET MVC application, you can take advantage of HTML Helpers to render standard HTML tags. For example, instead of typing this:

    <input name="inpSubmit" type="submit" value="Click Here!" />

    You can type this:

    <%= Html.SubmitButton("inpSubmit", "Click Here!") %>

    Over the long run, HTML Helpers can save you a lot time. But what if there isn’t an HTML Helper for a tag that you want to render? For example, imagine that you want to display a bulleted list of database records in a View. The HtmlHelper class doesn’t include a method that lets you render a bulleted list. Don’t give up. If the HTML Helper doesn't include a method that you need, just extend it!

    You can add new functionality to the HtmlHelper class by creating new extension methods. An extension method looks just like a normal instance method. However, unlike a normal instance method, you add extension methods to a class by defining the methods in a completely different class.

    In Visual Basic .NET, you create extension methods by creating a module and decorating the extension methods with a special attribute. In C#, you define extension methods in a static class and use the keyword this to indicate the class being extended.

    Here’s how you can add extension methods to the HtmlHelper class to display both ordered and unordered list of database records:

    Listing 1 – ListExtensions.vb (VB.NET)

       1: Imports System
       2: Imports System.Collections
       3: Imports System.Text
       4: Imports System.Web
       5: Imports System.Web.Mvc
       6: Imports System.Runtime.CompilerServices
       7:  
       8:  
       9: Namespace HtmlHelpers
      10:  
      11:     Public Module ListExtensions
      12:  
      13:         <Extension()> _
      14:         Public Function OrderedList(ByVal HtmlHelper As HtmlHelper, ByVal items As Object) As String
      15:             Return "<ol>" + ListExtensions.GetListItems(items) + "</ol>"
      16:         End Function
      17:  
      18:         <Extension()> _
      19:         Public Function UnorderedList(ByVal HtmlHelper As HtmlHelper, ByVal items As Object) As String
      20:             Return "<ul>" + ListExtensions.GetListItems(items) + "</ul>"
      21:         End Function
      22:  
      23:  
      24:         Private Function GetListItems(ByVal items As Object) As String
      25:             If items Is Nothing Then
      26:                 Throw New ArgumentNullException("items")
      27:             End If
      28:             If Not TypeOf items Is IEnumerable Then
      29:                 Throw New InvalidCastException("items must be IEnumerable")
      30:             End If
      31:  
      32:             Dim EnumItems As IEnumerable = CType(items, IEnumerable)
      33:             Dim builder As New StringBuilder()
      34:             For Each item As Object In EnumItems
      35:                 builder.AppendFormat("<li>{0}</li>", HttpUtility.HtmlEncode(item.ToString()))
      36:             Next
      37:             Return builder.ToString()
      38:         End Function
      39:  
      40:     End Module
      41: End Namespace

    Listing 1 – ListExtensions.cs (C#)

       1: using System;
       2: using System.Collections;
       3: using System.Text;
       4: using System.Web;
       5: using System.Web.Mvc;
       6:  
       7: namespace BulletedListHelper.HtmlHelpers
       8: {
       9:     public static class ListExtensions
      10:     {
      11:         public static string OrderedList(this HtmlHelper helper, Object items)
      12:         {
      13:             return "<ol>" + ListExtensions.GetListItems(items) + "</ol>";
      14:         }
      15:  
      16:         public static string UnorderedList(this HtmlHelper helper, Object items)
      17:         {
      18:             return "<ul>" + ListExtensions.GetListItems(items) + "</ul>";
      19:         }
      20:  
      21:  
      22:         private static string GetListItems(Object items)
      23:         {
      24:             if (items == null)
      25:                 throw new ArgumentNullException("items");
      26:             if (items is IEnumerable == false)
      27:                 throw new InvalidCastException("items must be IEnumerable");
      28:  
      29:             var enumItems = (IEnumerable)items;
      30:             var builder = new StringBuilder();
      31:             foreach (Object item in enumItems)
      32:                 builder.AppendFormat("<li>{0}</li>", HttpUtility.HtmlEncode(item.ToString()));
      33:             return builder.ToString();
      34:         }
      35:  
      36:     }
      37: }

    The ListExtensions class has two public methods: OrderedList() and UnorderedList(). You pass a collection of items to either method to display either a numbered or bulleted list of items. Notice that these methods return strings. Really, an HTML Helper method is nothing more than a method that renders a formatted string to the browser.

    After you create the extension methods, you can use the methods in a View like this:

    Listing 2 – Index.aspx

       1: <%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="BulletedListHelper.Index" %>
       2: <%@ Import Namespace="BulletedListHelper.HtmlHelpers" %>
       3:  
       4: <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
       5:   
       6:  
       7:    <h1>Movies (Ordered)</h1>
       8:  
       9:    <%= Html.OrderedList(ViewData.Model) %>
      10:  
      11:  
      12:    <h1>Movies (Unordered)</h1>
      13:  
      14:  
      15:    <%= Html.UnorderedList(ViewData.Model) %>
      16:  
      17:  
      18: </asp:Content>

    Notice that the BulletedList.HtmlHelpers namespace gets imported at the top of the file. The method Html.OrderedList() is used to render a numbered list and the method Html.UnorderedList() is used to render a bulleted list. Notice that these methods are being called on the HtmlHelper exposed by the Html property of the View just like any other extension method. When you open this View in a browser, you get the page in Figure 1:

    Figure 1 – Index.aspx Rendered with Custom HTML Helpers

    clip_image002

    Finally, the Index() method exposed by the HomeController in Listing 3 illustrates how you can pass a collection of movie records to the Index.aspx View. The movie records are retrieved by taking advantage of a Linq to SQL query.

    Listing 3 – HomeController.vb (VB.NET)

       1: Public Class HomeController
       2:     Inherits System.Web.Mvc.Controller
       3:  
       4:     Private db As New MoviesDataContext()
       5:  
       6:     Function Index()
       7:         Dim movies = From m In db.Movies Select m.Title
       8:         Return View(movies)
       9:     End Function
      10:  
      11:    
      12: End Class

    Listing 3 – HomeController.cs (C#)

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Linq;
       4: using System.Web;
       5: using System.Web.Mvc;
       6: using BulletedListHelper.Models;
       7:  
       8: namespace BulletedListHelper.Controllers
       9: {
      10:     public class HomeController : Controller
      11:     {
      12:  
      13:         private MoviesDataContext db = new MoviesDataContext();  
      14:  
      15:         public ActionResult Index()
      16:         {
      17:             var movies = from m in db.Movies select m.Title;
      18:             return View(movies);
      19:         }
      20:  
      21:     }
      22: }

    You can use this approach to render just about anything within an ASP.NET MVC View. For example, you can use a similar approach to create TreeViews, Menus, tabstrips, whatever.

    Click Here to Download the Source Code

  • TDD : Introduction to Moq

    In this post, I provide an introduction to Moq which is the newest of the Mock Object Frameworks. Moq is promoted by its creators as easier to learn and use than other Mock Object Frameworks such as Rhino Mocks and TypeMock Isolator.

    Moq takes advantage of recent VB.NET and C# language features such as lambdas and generics. When creating mock objects with Moq, you use lambda expressions to represent the methods and properties that you want to mock. The emphasis on lambdas enables Moq to provide a very clean syntax for representing expectations, parameter constraints, and return values.

    According to the authors of Moq, you can pronounce “Moq” either like “Mock-You” or like “Mock”. The name “Moq” comes from a combination of Mock and Linq. Since Moq uses lambda expressions and not Linq, the name “Mambda” might have been more accurate.

    Moq is actively maintained by Daniel Cazzulino. You can read Daniel Cazzuiono’s blog entries on Moq here:

    http://www.clariusconsulting.net/blogs/kzu/archive/category/1062.aspx

    This post is about Moq version 2.5.

    Some Background, Some Philosophy, and Some Controversy

    There is just something about Mock frameworks that generate controversy. There are now three popular Mock frameworks: Typemock Isolator, Rhino Mocks, and Moq. TypeMock Isolator has been criticized for being too powerful and Moq has been criticized for being too simple. Why the criticism?

    Some people have criticized Typemock Isolator for being too powerful. The criticism is that TypeMock Isolator might encourage bad software design because it enables you to be lazy about the architecture of your code. The argument is that building mockable code should force you to write well designed code. Since Typemock Isolator enables you to mock anything and everything, the tool does not lead you down the virtuous path to code nirvana. Typemock Isolator is like giving a nuclear weapon to a four year old child.

    While Typemock Isolator has been criticized for being too powerful, Moq has been criticized for being too simple. The complaint is that Moq mixes up mocking and stubbing. Understanding this criticism requires some background.

    Martin Fowler wrote an interesting and influential paper entitled Mocks Aren’t Stubs which you can read here:

    http://martinfowler.com/articles/mocksArentStubs.html

    In this paper, Fowler makes several distinctions. First, he distinguishes a stub from a mock. According to Fowler -- who uses Meszaros’ definitions here – stubs “provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.” Mocks, on the other hand, are “objects pre-programmed with expectations which form a specification of the calls they are expected to receive”.

    This distinction between stubs and mocks leads Fowler to distinguish between state verification and behavior verification. Stubs are most often used when performing state verification. When performing state verification, you are interested in determining whether a certain condition is true or false at the end of a test. Mocks, on the other hand, are most often used when performing behavior verification. When performing behavior verification, you are interested in how the mock objects interact. For example, you want to know whether a not a certain method was called on one mock object after a method was called on another mock object.

    Fowler makes a final distinction between classical TDD and mockist TDD. This final distinction concerns the “philosophy to the way testing and design play together”. A classical TDDer tends to use stubs and state verification. According to Fowler, the “classical TDD style is to use real objects if possible and a double if it's awkward to use the real thing.” A mockist TDDer, on the other hand, almost always uses mocks and behavior verification. A mockist TDDer “will always use a mock for any object with interesting behavior.”

    A classical TDDer uses a Mock Object Framework to make it easier to build unit tests. If the code that you are testing has a dependency on some other object, you can create a quick stub for the inconvenient object and continue writing your unit test. From the perspective of a classical TDDer, that is what a Mock Object Framework is for.

    A mockist TDDer inhabits a completely different world. A mockist TDDer believes that the design of an application should be driven by the way that you expect objects in your application to interact. A mockist TDDer uses mock objects to pantomime the final application. Therefore, everything gets mocked in a test.

    Moq has been criticized for not respecting the needs of both the classical TDDer and the mockist TDDer. Moq makes it easy to make stubs and not mocks. This is either a good thing or bad thing depending on what you want out of a Mock Object Framework.

    Daniel Cazzulino, the primary author of Moq, is unapologetically a classical TDDer. There is a very interesting exchange between Daniel Cazzulino and Ayende (the author of Rhino Mocks) on this topic at:

    http://www.ayende.com/Blog/archive/2007/12/19/Moq-Mocking-in-C-3.0.aspx

    Daniel Cazzulino argues that most developers, if they are using Test-Driven Development at all, don’t distinguish between classical TDD and mockist TDD. And, in fact, the distinction isn’t as clear cut as Martin Fowler describes. You can read Daniel Cazzulino’s paper, entitled Mocks, Stubs and Fakes: it's a continuum by visiting Daniel Cazzulino’s blog at:

    http://www.clariusconsulting.net/blogs/kzu/archive/2007/12/21/47152.aspx

    Daniel Cazzulino does not use the Fowler/ Meszaros distinctions between mocks and stubs. He claims that most developers use the word “mocks” when referring to what Fowler/Meszaros would refer to as stubs and that we should respect the everyday language use of these words. Since this post in on Moq, for the remainder of this post I will knowingly and actively conflate mocks and stubs out of respect for Daniel Cazzulino.

    Installing and Setting Up Moq

    Moq is maintained as a Google Code project. You can download the Moq binaries and API documentation here:

    http://code.google.com/p/moq/

    After you download the Moq binaries from Google Code, make sure that you unblock the archive by right-clicking the file, selecting Properties, and pushing the Unblock button (see Figure 1). If you forget to do this, you run into confusing security issues when you try to use Moq within Visual Studio.

    clip_image002

    The archive includes an assembly named Moq.dll. You need to add this assembly to your test project in Visual Studio. You also need to add the Moq namespace to your test classes.

    What Can Be Mocked?

    You can use Moq to create mocks from both interfaces and existing classes. There are some requirements on the classes. The class can’t be sealed. Furthermore, the method being mocked must be marked as virtual. You cannot mock static methods (use the adaptor pattern to mock a static method).

    These limitations are the same as the limitations you face when working with Rhino Mocks. Both Moq and Rhino Mocks use proxy classes under the covers. Indeed, both frameworks derive from the same Castle DynamicProxy code base.

    Mocking Methods and Properties

    Imagine that you are building a database-driven web application. Imagine, for example, that you are creating an online store. You want to concentrate on writing all of the business logic for your store before doing anything else. In particular, you don’t want to devote any time to writing your data access components before writing your business components.

    This is a good situation to take advantage of a Mock Object Framework. In this situation, you can create an interface that describes how you want your data access component to look. Then, you can simply mock the interface and take advantage of the mock while testing your business logic. The mock enables you to avoid writing code until you are really ready to do it.

    The first interface in Listing 1, named IProductRepository, represents two methods. The first method, Select(), returns all of the products from the database. The second method, Get(), returns a particular product by matching the product’s Id. Listing 1 also contains an interface, named IProduct, that represents a particular product.

    Listing 1 – IProductRepository.cs

       1: using System;
       2: using System.Collections.Generic;
       3:  
       4: namespace MoqSamples.Models
       5: {
       6:     public interface IProductRepository
       7:     {
       8:         List<IProduct> Select();
       9:         IProduct Get(int id);
      10:     }
      11:  
      12:     public interface IProduct
      13:     {
      14:         int Id {get; set;}
      15:         string Name { get; set; }
      16:     }
      17: }

    Let’s start with mocking the IProduct interface. The following code creates a product that has an Id property with the value 1 and a Name property with the value “Bushmills”:

       1: // Mock a product
       2: var newProduct = new Mock<IProduct>();
       3: newProduct.ExpectGet(p => p.Id).Returns(1);
       4: newProduct.ExpectGet(p => p.Name).Returns("Bushmills");

    The first line of code creates the mock from the IProduct interface. The Mock class is a class from the Moq framework. The Mock class has a generic constructor that accepts the type of the interface to create.

    Next, the Id property is setup to return the value 1 and the Name property is setup to return the value “Bushmills”. Notice how lambda expressions are used to represent the Id and Name property. The advantage of using a lambda expression to represent a property instead of a string is that refactoring tools such as Resharper can refactor the properties automatically.

    After you create the mock newProduct, you can use the mock just as if you had actually implemented the IProduct interface. For example, the following Assert executes successfully:

    Assert.AreEqual("Bushmills", newProduct.Object.Name);

    Notice that you must use newProduct.Object when referring to the mock newProduct. The newProduct variable represents the proxy class and the newProduct.Object variable represents the actual newProduct class.

    Now let’s mock the IProductRepository interface. The following lines of code create a mock IProductRepository that returns newProduct when its Get() method is called:

       1: // Mock product repository
       2: var productRepository = new Mock<IProductRepository>();
       3: productRepository
       4:    .Expect(p => p.Get(1))
       5:    .Returns(newProduct.Object);

    The first line of code creates the mock by passing IProductRepository to the Mock class’s generic constructor. The next line setups the Get() method to return the mock newProduct. Notice that the Get() method call is represented with a lambda.

    After you create IProductRepository mock, you can use it in your tests like this:

       1: // Act
       2:  
       3: var productReturned = productRepository.Object.Get(1);
       4:  
       5: // Assert
       6:  
       7: Assert.AreEqual("Bushmills", productReturned.Name);
       8:  

    When you call the Get() method with the value 1, the newProduct object is returned. If you call Get() with any other value than 1 then a Null value is returned. If you want the Get() method to return newProduct regardless of the argument passed to the Get() method, then you can create the mock IProductRepository with the following lines of code:

       1: // Mock product repository
       2: var productRepository = new Mock<IProductRepository>();
       3: productRepository
       4:   .Expect(p => p.Get(It.IsAny<int>()))
       5:   .Returns(newProduct.Object);

    Notice that the It.IsAny<int>() constraint is passed to the Get() method when setting up the method expectation. This argument causes the mock Get() method to accept any integer value and return the newProduct object.

    You can even specify fancy custom constraints by taking advantage of a lambda expression. Consider the following code:

       1: // Mock product repository
       2: var productRepository = new Mock<IProductRepository>();
       3: productRepository
       4:    .Expect(p => p.Get(It.Is<int>(id => id>0 && id<6)))
       5:    .Returns(newProduct.Object);

    This code returns the newProduct object only when the id parameter passed to the Get() method has a value between 0 and 6. This constraint is specified within a lambda expression passed to the It.Is() method.

    Moq is Mockist Friendly (At Least a Little Bit)

    The Moq framework can be used to perform limited behavior verification. For example, you can use Moq to detect whether a particular method was called at least once after calling another method.

    Moq’s support for behavior verification is limited. Unlike other Mock Object Frameworks, such as Rhino Mocks and Typemock Isolator, you cannot use Moq to test complex interactions between objects. Moq does not support the same kind of record and replay code blocks supported by Rhino Mocks and Typemock Isolator.

    When would you want to perform behavior verification? Martin Fowler provides the example of a memory cache. He writes “The whole point of a cache is that you can't tell from its state whether the cache hit or missed - this is a case where behavior verification would be the wise choice for even a hard core classical TDDer” (quotation is from Mocks Aren’t Stubs).

    Imagine that you have implemented a ProductRepository class and that the class includes a method named GetProduct() that retrieves a product. The method first attempts to get the product from the cache. If the method is not successful then the method retrieves the product from the database. The ProductRepository class is contained in Listing 2:

    Listing 2 – ProductRepository

       1: using System;
       2: using System.Web;
       3:  
       4: namespace MoqSamples.Models
       5: {
       6:     public class ProductRepository : IProductRepository
       7:     {
       8:         private ProductCache _cache;
       9:  
      10:         public ProductRepository(ProductCache cache)
      11:         {
      12:             _cache = cache;
      13:         }
      14:  
      15:         public virtual IProduct GetProduct(int id)
      16:         {
      17:             var product = _cache.Get(id);
      18:             if (product == null)
      19:             {
      20:                 product = this.Get(id);
      21:                 _cache.Set(id, product);
      22:             }
      23:             return product;
      24:         }
      25:         
      26:         public virtual IProduct Get(int id)
      27:         {
      28:             throw new NotImplementedException();
      29:         }
      30:  
      31:         public System.Collections.Generic.List<IProduct> Select()
      32:         {
      33:             throw new NotImplementedException();
      34:         }
      35:  
      36:     }
      37: }

    A ProductCache object is passed to the ProductRepository class through construction dependency injection. The ProductRepository GetProduct() method first attempts to grab a product from this cache object and, if it fails, it calls the Get() method to grab the product from the database. Notice that I haven’t bothered to implement the Get() method. That’s okay since we will be mocking it anyway.

    The ProductCache class is a strongly typed wrapper around the normal ASP.NET System.Web.Caching.Cache object. The code for the ProductCache is contained in Listing 3:

    Listing 3 – ProductCache.cs

       1: using System;
       2: using System.Web;
       3:  
       4: namespace MoqSamples.Models
       5: {
       6:     public class ProductCache
       7:     {
       8:         public virtual IProduct Get(int id)
       9:         {
      10:             return (IProduct)HttpContext.Current.Cache["product_" + id];
      11:         }
      12:  
      13:         public virtual void Set(int id, IProduct product)
      14:         {
      15:             HttpContext.Current.Cache["product_" + id] = product;
      16:         }
      17:     }
      18: }

    Notice that both methods of the ProductCache class are marked as virtual. You must mark methods as virtual when you want to be able to mock them.

    We can test whether the ProductCache is used correctly by taking advantage of Moq when creating a unit test. The unit test in Listing 4 demonstrates how to use Moq to perform simple behavior verification:

    Listing 4 – ProductTest.cs

       1: using System;
       2: using Microsoft.VisualStudio.TestTools.UnitTesting;
       3: using MoqSamples.Models;
       4: using Moq;
       5: using System.Web;
       6:  
       7: namespace MoqSamplesTests
       8: {
       9:     [TestClass]
      10:     public class ProductTest
      11:     {
      12:         [TestMethod]
      13:         public void TestCache()
      14:         {
      15:             // Setup
      16:             var mockCache = new Mock<ProductCache>();
      17:             var mockProducts = new Mock<ProductRepository>(mockCache.Object);
      18:             var mockProduct = new Mock<IProduct>();
      19:  
      20:             // Expectations
      21:             mockCache
      22:                 .Expect(c => c.Get(1))
      23:                 .Returns<IProduct>(null)
      24:                 .Verifiable(); // No product in cache
      25:  
      26:             mockProducts
      27:                 .Expect(p => p.Get(1))
      28:                 .Returns(mockProduct.Object)
      29:                 .Verifiable(); // Get product from db
      30:  
      31:             mockCache
      32:                 .Expect(c => c.Set(1, mockProduct.Object))
      33:                 .Verifiable(); // Set product in cache
      34:  
      35:             // Reality
      36:             var product = mockProducts.Object.GetProduct(1);
      37:  
      38:             // Assert
      39:             mockProducts.Verify(); 
      40:             mockCache.Verify();  
      41:         }
      42:  
      43:     }
      44: }

    In Listing 4, the setup section is used to setup three mock objects: mockCache, mockProducts, and mockProduct. Next, in the expectations section, expectations are created that represent how the methods of these mock objects are expected to be called. Notice that all three expectations include a call to a method named Verifiable(). When the Verify() method is called at the end of the test, if any of the expectations marked as verifiable have not been called, then an exception is thrown.

    The test in Listing 4 checks that the following expectations are met when the GetProduct() method is called:

    · The ProductCache.Get() method is called

    · The ProductRepository.Get() method is called

    · The ProductCache.Set() method is called

    In other words, when the GetProduct() method is first called, we want to make sure that the product being requested is added to the cache.

    Again, it is important to emphasize the limitations of Moq here. You can’t use Moq to verify whether or not these methods were called in a particular order or a particular number of times. Moq is good at state verification and not behavior verification.

    Conclusion

    I like Moq. I like how Moq takes advantage of lambdas when building expectations. I find this aspect of the framework very intuitive. If you are a classical TDDer then I think that you will discover a lot to like in Moq.

    If you need to mock objects that cannot be handled by Moq, such as static methods or sealed classes, then you should consider using Typemock Isolator. If you are working with legacy code, then you might not have the luxury of working with a well-designed, loosely coupled application. In that case, Typemock Isolator might be your only viable choice.

    Finally, if you lean more towards mockist TDD than classical TDD, then you will find Rhino Mocks more appealing. Rhino Mocks provides a richer framework for performing behavior verification.

  • ASP.NET MVC Book List

    Stack of BooksI've been collecting book recommendations on software design principles and patterns, test-driven development, and agile development. I wanted to share my current reading list with you in case you are interested in learning more about these topics. If you have any additional book recommendations -- please share!

    Here's my current list of books:

    Software Design Principles and Patterns Books

    1. Design Patterns : Elements of Reusable Object-Oriented Software
      by Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides
      This is the book known as the Gang of Four book and it is considered required reading if you want to learn about software design principles and patterns. Several people warned me that this book would be dense. Several people also warned me that the pattern terminology used in the book has changed significantly since the book was published in 1994. So far, this book has not caused any brain injury and I haven’t found it overly difficult to read.
    2. Head First Design Patterns
      by Elisabeth Freeman, Eric Freeman, Bert Bates, and Kathy Sierra
      This book makes the previous book understandable. It has really clear samples of each of the design patterns. This is a great book – it was a joy to read. There are only two things that I don’t like about it. I wish that it did not contain all of the cartoons. Also, I wish the samples were C#/VB.NET instead of Java. Troy Goode originally recommended this book to me.
    3. Framework Design Guidelines
      by Krzysztof Cwalina and Brad Abrams
      If you want to understand why the .NET framework was designed in the way that it is designed, then this is the book to read. The book introduces great phrases like “Fall into the pit of success”. By reading this book, I also learned the origin of the terminology Pascal Casing and Camel Casing.
    4. Refactoring: Improving the Design of Existing Code
      by Martin Fowler
      Martin Fowler’s book got everyone talking about better software design through refactoring. The book contains a catalog of more than 70 types of refactorings.
    5. Refactoring to Patterns
      by Joshua Kerievsky
      As the title suggests, this book combines a discussion of patterns and refactoring. I’ve been told that this is a super important book and that I should read it immediately.
    6. Patterns of Enterprise Application Architecture
      by Martin Fowler
      Another massively important book written by Martin Fowler. Contains a catalog of over 40 patterns that you should know when building enterprise applications.
    7. Domain Driven Design: Tackling Complexity in the Heart of Software
      by Eric Evans
      This book is devoted to the process of building software through patterns and refactoring.

    Test-Driven Development Books

    1. Working Effectively with Legacy Code
      by Michael Feathers
      This book has received recommendations all over the place. It is the next book that I plan to read.
    2. xUnit Test Patterns
      by Gerard Meszaros
      This is a big, intimidating book. This book is supposed to do a good job clarifying testing terminology such as the distinction between mocks, stubs, dummies, doubles, etc.
    3. Test-Driven Development in Microsoft .NET
      by James W. Newkirk and Alexei A. Vorontsov
      This was one of the first books on Test-Driven Development that I read. I really liked the organization of the book. I like how the book presents walkthroughs of using TDD when testing ADO.NET and user interfaces.
    4. Test-Driven Development: By Example
      by Kent Beck
      The Kent Beck book on Test-Driven Development. This is a really enjoyable book to read. I was surprised that Kent Beck takes a very practical approach to using TDD. He even presents a few situations in which TDD does not make sense (testing concurrency and security).
    5. Pragmatic Unit Testing in C# with NUnit, 2nd Edition
      by Andy Hunt, Dave Thomas, and Matt Hargett
      This book covers using NUnit when building ASP.NET 2.0 applications.

    Agile Books

    1. Extreme Programming Explained: Embrace Change
      by Kent Beck and Cynthia Andres
      The founding book of XP. I’ve been warned that the first edition of this book differs significantly from the second edition.
    2. Agile Principles, Patterns, and Practices in C#
      by Robert C. Martin and Micah Martin
      This is a dense but valuable book. The authors (father and son) do a great job of delving deep into the subject of software design principles and patterns. The code samples in this book are written in C#. Brian Henderson recommended that I read this book.

    Almost all of these books were recommended by friends or colleagues. If you know of a good book related to these topics, please leave a comment.

  • I’m Now Working at Microsoft

    It’s official, I’m now working at Microsoft. I have an office in building 42 and they have supplied me with a phone, an email account, and a computer. I even have a whiteboard in case inspiration hits me (it currently is blank).

    I’m excited about the job. I was hired to work with the ASP.NET MVC team to build content around ASP.NET MVC and act as a liaison with the MVC developer community. I get to play with what Phil Haack and the rest of the ASP.NET MVC team is building when it is brand spanking new.  I get to interact with the developer community and show off what Microsoft is creating.

    I’m excited about ASP.NET MVC. New frameworks for building web applications emerge very rarely. I’ve been building websites by taking advantage of Microsoft technologies for a very long time. I started with IDC and HTX templates, progressed to Active Server Pages, and then made the leap to ASP.NET. ASP.NET MVC is still very much a part of ASP.NET, but it embodies a very different approach to building web applications.

    ASP.NET MVC is an alternative, but not a replacement, to using Web Forms when building applications with the ASP.NET framework. If you are an Agile developer, or you are excited about ideas from the Agile community, then you’ll find ASP.NET MVC very attractive.

    I’m very much a mainstream ASP.NET developer. I like Web Forms and I have built a lot of really great websites by taking advantage of Web Forms. However, I think that there are many valuable ideas and practices that mainstream ASP.NET developers can learn from the Agile world. You might have noticed that I have been posting entries on Test-Driven Development, Mock Frameworks, and Design Principles and Patterns on this blog during the last few months. One of the primary goals of my new job is to get mainstream ASP.NET developers excited about these ideas.

    I admit, I am a little intimidated about what I need to learn. This is new territory for me. I’m leaving my safe home of ASP.NET Web Forms and entering the savage wilderness. I suspect, however, that this adventure will be worth it.

    Oh, one last thing. I redesigned the look of this blog. I added a prominent picture of myself to the banner thinking deep thoughts (What should I put on that whiteboard? What should I put on that whiteboard?). Let me know what you think of the new design.