January 2011

Volume 26 Number 01

ASP.NET Dynamic Data - Build a Data-Driven Enterprise Web Site in 5 Minutes

By James E. Henry | January 2011

For years, developers have wrestled with the tedious tasks of building data layers with Create, Read, Update and Delete (CRUD) functionality to be consumed by the UI. I can personally remember a project from more than 10 years ago that required me to write code that automatically generated business and data layers from a Rational Rose object model. It was a lot of work.

A few years later Microsoft introduced the Microsoft .NET Framework and the DataSet object. With the DataSet designer, you could connect a strongly typed DataSet to a back-end database such as SQL Server and then work with the DataSet throughout the application. This minimized the need to work directly with SQL, but it still required the manual creation of Web pages to display data from the database.

Fast-forward a few more years and Microsoft introduced the Entity Framework. This Object Relational Mapping (ORM) framework uses LINQ to abstract a database schema and presents it to an application as a conceptual schema. Like the DataSet, this technology also minimized the need to work directly with SQL. However, it still required the manual creation of Web pages to display data.

Now Microsoft has introduced ASP.NET Dynamic Data—a combination of the Entity Framework and ASP.NET Routing—which allows an application to respond to URLs that do not physically exist. With these features you can create a production-ready, data-driven Web site in just a few minutes. In this article I’ll show you how.

Getting Started

Just to set up the scenario, let’s say I’m building an intranet Web site for a fictional company called Adventure Works. This Web site allows the company to manage employee information.

The company’s data is stored in a Microsoft SQL Server 2008 database. (You can download and install the database from msftdbprodsamples.codeplex.com.)

Now, open Microsoft Visual Studio 2010 and create a new ASP.NET Dynamic Data Entities Web Site C# project.

The ASP.NET Dynamic Data Entities Web Site project type takes advantage of ASP.NET Routing and the Entity Framework to enable you to quickly create data-driven Web sites. To get this functionality, you need to add an Entity Framework data model to the project. To do this, choose Add New Item from the Web site menu. In the Add New Item dialog box, choose ADO.NET Entity Data Model. Name it HumanResources.edmx.

Visual Studio will then prompt you to add the model to the App_Code folder. Choose Yes and allow it to make this change. When you create Web site projects, Visual Studio dynamically compiles all code that is placed in the App_Code folder. In the case of the Entity Data Model, Visual Studio automatically generates a partial class for the data context and partial classes for the entities. In this example, it places the code in a file named HumanResources.Designer.cs.

Next, the Entity Data Model Wizard appears, as shown in Figure 1. Choose Generate from Database and click Next.

image: Starting the Entity Data Model Wizard

Figure 1 Starting the Entity Data Model Wizard

Now choose a connection to the Adventure Works database. If a connection doesn’t already exist, you need to create a new one. Figure 2 shows a connection to AdventureWorks on a SQL Server instance named Dev\BlueVision.

image: Configuring the Data Connection

Figure 2 Configuring the Data Connection

On the next page you can choose all tables in the Human Resources schema. The name of the schema appears in parenthesis. Figure 3 shows some of the tables that are selected.

image: Selecting Tables for the Human Resources Schema in Visual Studio

Figure 3 Selecting Tables for the Human Resources Schema in Visual Studio

When you click Finish, Visual Studio automatically generates entities from the tables that you chose for your project. Figure 4 shows seven entities that Visual Studio generated from the database schema. Visual Studio used the foreign key constraints in the database to create relationships between the entities. For example, the Employee entity participates in a one-to-many relationship with the JobCandidate entity. This means that an employee can be a candidate for multiple jobs within the company.

iamge: Entities Generated from Database Tables in Visual Studio

Figure 4 Entities Generated from Database Tables in Visual Studio

Also note that the EmployeeDepartmentHistory entity joins the Employee and Department entities. Had the EmployeeDepartmentHistory table contained only the fields that were necessary for joining the Employee and Department tables, Visual Studio would have simply omitted the EmployeeDepartmentHistory entity. This would have allowed direct navigation between the Employee and Department entities.

Using ASP.NET Routing

ASP.NET Routing allows an application to respond to URLs that do not physically exist. For example, two URLs—http://mysite/Employee and http://mysite/Department—could be directed to a page at http://mysite/Template.aspx. The page itself could then extract information from the URL to determine whether to display a list of employees or a list of departments, with both views using the same display template.

A route is simply a URL pattern that’s mapped to an ASP.NET HTTP handler. It’s the handler’s responsibility to determine how the actual URL maps to the interpreted pattern. ASP.NET Dynamic Data uses a route handler named DynamicDataRouteHandler that interprets placeholders named {table} and {action} in URL patterns. For example, a handler could use this URL pattern:

http://mysite/{table}/{action}.aspx

That pattern could then be used to interpret the URL http://mysite/Employee/List.aspx.

Employee is processed as the {table} placeholder and List is processed as the {action} placeholder. The handler could then display a list of Employee entity instances. DynamicDataRouteHandler uses the {table} placeholder to determine the name of the entity to display, and it uses the {action} parameter to determine the page template used to display the entity.

The Global.asax file contains a static method named RegisterRoutes that gets called when the application first starts, as shown here:

public static void RegisterRoutes(RouteCollection routes) {
  // DefaultModel.RegisterContext(typeof(YourDataContextType), 
  //   new ContextConfiguration() { ScaffoldAllTables = false });
  routes.Add(new DynamicDataRoute("{table}/{action}.aspx") {
    Constraints = new RouteValueDictionary(
    new { action = "List|Details|Edit|Insert" }),
    Model = DefaultModel
  });
}

The first thing you need to do is uncomment the code that calls the RegisterContext method of the DefaultModel instance. This method registers the Entity Framework data context with ASP.NET Dynamic Data. You need to modify the line of code as follows:

DefaultModel.RegisterContext(
    typeof(AdventureWorksModel.AdventureWorksEntities), 
    new ContextConfiguration() { ScaffoldAllTables = true });

The first parameter specifies the type of the data context, which is AdventureWorksEntities in this example. By setting the ScaffoldAllTables property to true, you allow all entities to be viewed on the site. Had you set this property to false, you’d need to apply the ScaffoldTable(true) attribute to each entity that should be viewed on the site. (In practice, you should set the ScaffoldAllTables property to false to prevent the accidental exposure of the entire database to end users.)

The Add method of the RouteCollection class adds a new Route instance to the route table. With ASP.NET Dynamic Data, the Route instance is actually a DynamicDataRoute instance. The DynamicDataRoute class internally uses DynamicDataRouteHandler as its handler. 
The parameter passed to the constructor of DynamicDataRoute represents the pattern that the handler should use to process physical URLs. A constraint is also set to limit the {action} values to List, Details, Edit or Insert.

This is theoretically all you need to do to use ASP.NET Dynamic Data. If you build and run the application, you should see the page shown in Figure 5.

image: A Basic ASP.NET Dynamic Data Site

Figure 5 A Basic ASP.NET Dynamic Data Site

Supporting Metadata

One thing you should notice is that the entity names are identical to the table names that they represent. In code and in the database this might be fine, however, usually this is not the case in the UI.

ASP.NET Dynamic Data makes it easy to change the name of a displayed entity by applying the DisplayName attribute to the class that represents that entity. Because the entity classes are contained in a file that’s automatically regenerated by Visual Studio, you should make any code changes to partial classes in a separate code file. Therefore, add a new code file named Metadata.cs to the App_Code folder. Then add the following code to the file:

using System;
  using System.Web;
  using System.ComponentModel;
  using System.ComponentModel.DataAnnotations;
  namespace AdventureWorksModel {
    [DisplayName("Employee Addresses")]
    public partial class EmployeeAddress { }
  }

Then rebuild and run the application. You should notice that EmployeeAddresses is now Employee Addresses.

Similarly, you can change the names of EmployeeDepartmentHistories, EmployeePayHistories and JobCandidates to more appropriate names.

Next click the Shifts link. This displays a list of employee shifts. I’ll change the names StartTime and EndTime to Start Time and End Time, respectively.

Another issue is that the Start Time and End Time values show both the date and the time. In this context, the user really only needs to see the time, so I’ll format the Start Time and End Time values so that they display times only. An attribute named DataType allows you to specify a more specific data type for a field, such as EmailAddress or Time. You apply the DataType attribute to the field to which it applies.

First, open the Metadata.cs file and add the following class definitions:

[MetadataType(typeof(ShiftMetadata))]
public partial class Shift { }
public partial class ShiftMetadata { }

Notice that an attribute named MetadataType is applied to the Shift class. This attribute allows you to designate a separate class that contains additional metadata for an entity’s fields. You can’t apply the additional metadata directly to members of the Shift class because you can’t add the same class members to more than one partial class. For example, the Shift class defined in HumanResources.Designer.cs already has a field named StartTime. So you can’t add the same field to the Shift class defined in Metadata.cs. Also, you shouldn’t manually modify HumanResources.Designer.cs because that file gets regenerated by Visual Studio.

The MetadataType attribute allows you to specify an entirely different class so that you can apply metadata to a field. Add the following code to the ShiftMetadata class:

[DataType(DataType.Time)]
  [Display(Name = "Start Time")]
  public DateTime StartTime { get; set; }

The DataType attribute specifies the Time enumerated value to indicate that the StartTime field should be formatted as a time value. Other enumerated values include PhoneNumber, Password, Currency and EmailAddress, to name a few. The Display attribute specifies the text that should be displayed as the field’s column when the field is displayed in a list, as well as the field’s label when it’s displayed in edit or read-only mode.

Now rebuild and run the application. Figure 6 shows the result of clicking the Shifts link.

image: Revised Shifts Page Using MetadataType Definitions

Figure 6 Revised Shifts Page Using MetadataType Definitions

You can add similar code to change the appearance of the EndTime field.

Now let’s take a look at the JobCandidates link. The Employee column displays values for the NationalIDNumber field. This might not be useful. Although the Employee database table doesn’t have a field for an employee’s name, it does have a field for an employee’s login, which is LoginID. This field might provide more useful information to a user of this site.

To do this, I’ll again modify the metadata code so that all Employee columns display the value of the LoginID field. Open the Metadata.cs file and add the following class definition:

[DisplayColumn("LoginID")]
public partial class Employee { }

The DisplayColumn attribute specifies the name of the field from an entity that should be used to represent instances of that entity. Figure 7 shows the new list with LoginIDs.

image: Identifying Employees with LoginIDs

Figure 7 Identifying Employees with LoginIDs


Starting with the Microsoft .NET Framework 4, we recommend that you use the Display attribute instead of the DisplayName attribute from the .NET Framework 3.5. DisplayName is obviously still in the framework, but we recommend against it whenever the Display attribute can be used.

There are two reasons for preferring Display instead of DisplayName. First, the Display attribute supports localization while the DisplayName attribute does not.

Second, the Display attribute allows you to control all kinds of things. For example, you can control the text for the various ways a field can be displayed (prompt, header and so on), whether a field shows up as a filter, or whether the field is shown in the scaffold (AutoGenerate=false turns it off).

So, while the code shown in the examples in this article is completely valid, we recommend that you replace DisplayName and ScaffoldColumn with the Display attribute. You still need to use ScaffoldTable and the DisplayName attribute at the class level.

Following these recommendations is best because other teams at Microsoft support the DataAnnotations namespace (WCF RIA Services), and following these practices will make sure code works with them.


The ContactID field of the Employee entity actually refers to a row in the Contact table, which is part of the Person schema in the database. Because I didn’t add any tables from the Person schema, Visual Studio allows direct editing of the ContactID field. To enforce relational integrity, I’ll prevent editing of this field while still allowing it to be displayed for reference. Open the Metadata.cs file and modify the Employee class by applying the following MetadataType attribute:

[DisplayColumn("LoginID")]
[MetadataType(typeof(EmployeeMetadata))]
public partial class Employee { }

Then define the EmployeeMetadata class as follows:

public partial class EmployeeMetadata {
  [Editable(false)]
  public int ContactID { get; set; }
}

The Editable attribute specifies whether or not a field is editable in the UI.

Next, I’ll add metadata to the EmployeePayHistory entity to display the Rate field as Hourly Rate, as well as format the field’s value as currency. Add the following class definitions to the Metadata.cs file:

[MetadataType(typeof(EmployeePayHistoryMetadata))]
public partial class EmployeePayHistory { }
public partial class EmployeePayHistoryMetadata {
  [DisplayFormat(DataFormatString="{0:c}")]
  [Display(Name = "Hourly Rate")]
  public decimal Rate { get; set; }
}

Customizing Templates

The Visual Studio project contains a folder named FieldTemplates. This folder contains user controls for editing fields of various data types. By default, ASP.NET Dynamic Data associates a field with a user control that has the same name as the field’s associated data type. For example, the Boolean.ascx user control contains the UI for displaying Boolean fields, while the Boolean_Edit.ascx user control contains the UI for editing Boolean fields.

Alternatively, you can apply the UIHint attribute to a field to ensure that a different user control is used. I’ll add a custom field template to display a Calendar control for editing of the Employee entity’s BirthDate field.

In Visual Studio, add a new Dynamic Data Field item to the project and name it Date.ascx. Visual Studio automatically adds a second file named Date_Edit.ascx to the FieldTemplates folder. I’ll first replace the content of the Date_Edit.ascx page with the following markup:

<asp:Calendar ID="DateCalendar" runat="server"></asp:Calendar>

I’ll then modify the Date_Edit.ascx.cs file with the complete class definition shown in Figure 8.

Figure 8 Custom Date_EditField Class

public partial class Date_EditField : System.Web.DynamicData.FieldTemplateUserControl {
  protected void Page_Load(object sender, EventArgs e) {
    DateCalendar.ToolTip = Column.Description;
  }
  protected override void OnDataBinding(EventArgs e) {
    base.OnDataBinding(e);
    if (Mode == DataBoundControlMode.Edit && 
        FieldValue != null) {
      DateTime date = DateTime.MinValue;
      DateTime.TryParse(FieldValue.ToString(), out date);
      DateCalendar.SelectedDate = 
        DateCalendar.VisibleDate = date;
    }
  }
    
  protected override void ExtractValues(
    IOrderedDictionary dictionary) {
    dictionary[Column.Name] = ConvertEditedValue(
      DateCalendar.SelectedDate.ToShortDateString());
  }
  public override Control DataControl {
    get {
      return DateCalendar;
    }
  }
}

I override the OnDataBinding method to set the SelectedDate and VisibleDate properties of the Calendar control to the value of the FieldValue field. The FieldValue field is inherited from FieldTemplateUserControl, and it represents the value of the data field being rendered. I also modify the ExtractValues overridden method to save any changes to the SelectedDate property to a dictionary of field name-value pairs. ASP.NET Dynamic Data uses the values in this dictionary to update the underlying data source.

Next, I need to inform ASP.NET Dynamic Data to use the Date.ascx and Date_Edit.ascx field templates for the BirthDate field. I can accomplish this in one of two ways. First, I can apply the UIHint attribute as follows:

[UIHint("Date")]
public DateTime BirthDate { get; set; }

Alternatively, I can apply the DateType attribute as follows:

[DataType(DataType.Date)]
public DateTime BirthDate { get; set; }

The DataType attribute provides automatic mapping by matching the data type name to the name of the user control. The UIHint attribute gives you greater control in situations where the field type doesn’t match the name of the user control. Figure 9 shows the result of editing an employee.

image: Customized Employee Edit Form

Figure 9 Customized Employee Edit Form

If you change the birth date of the selected employee and click Update, the new data will be saved to the database.

The PageTemplates folder contains page templates that render the appropriate views for entities. By default, five page templates are supported: List.aspx, Edit.aspx, Details.aspx, Insert.aspx and ListDetails.aspx. The List.aspx page template renders a UI for displaying entities as tabular data. The Details.aspx page template renders a read-only view of an entity, while the Edit.aspx page template displays an editable view of an entity. The Insert.aspx page renders an editable view with default field values. The ListDetails.aspx page template allows you to view a list of entities as well as details for the selected entity on a single page.

As I mentioned earlier in the article, ASP.NET Dynamic Data automatically routes URL requests to the appropriate page by examining the value of the {action} parameter defined for the route. For example, if ASP.NET Dynamic Data evaluates an {action} parameter as List, it uses the List.aspx page template to display a list of entities. You can alter the existing page templates or add a new one to the folder. If you add a new one, you must ensure that you add it to the route table in the Global.asax file.

The EntityTemplates folder contains templates for displaying entity instances in read-only, edit and insert modes. By default, this folder contains three templates named Default.ascx, Default_Edit.ascx and Default_Insert.ascx, which display entity instances in read-only, edit and insert mode, respectively. To create a template that applies to only a specific entity, simply add a new user control to the folder and give it the name of the entity 
set. For example, if you add a new user control named Shifts.ascx to the folder, ASP.NET Dynamic Data uses this user control to render the read-only mode for a Shift entity (Shifts entity set). Similarly, Shifts_Edit.ascx and Shifts_Insert.ascx render the edit and insert modes of the Shift entity, respectively.

For each list of entities, ASP.NET Dynamic Data uses the entity’s foreign key fields, Boolean fields and enumeration fields to build a filter list. The filters are added as DropDown controls to the list page, as shown in Figure 10.

image: Including Data Filters on the Page

Figure 10 Including Data Filters on the Page

For Boolean filters, the DropDownList control simply contains three values: All, True and False. For enumeration filters, the DropDownList control contains all enumerated values. For foreign key filters, the DropDownList control contains all distinct foreign key values. The filters are defined as user controls in the Filters folder. By default, only three user controls exist: Boolean.ascx, Enumeration.ascx and ForeignKey.ascx.

Ship It!

Although this was a fictional scenario, you’ve seen that it’s entirely possible to create a fully operational Human Resources Web site in just a few minutes. I then proceeded to enhance the UI by adding metadata and a custom field template.

ASP.NET Dynamic Data provides out-of-the-box functionality that allows you to get a site up and running quickly. However, it’s also entirely customizable, allowing it to meet the needs of individual developers and organizations. ASP.NET Dynamic Data support for ASP.NET Routing allows you to reuse page templates for CRUD operations. If you’ve become frustrated with having to continually perform the tedious tasks of implementing CRUD pages on every Web application project, then ASP.NET Dynamic Data should make your life a lot easier.         


James Henry  is an independent software developer for BlueVision LLC, a company that specializes in Microsoft technology consulting. He’s the author of “Developing Business Intelligence Solutions Using Information Bridge and Visual Studio .NET” (Blue Vision, 2005) and “Developing .NET Custom Controls and Designers Using C#” (Blue Vision, 2003). He can be reached at msdnmag@bluevisionsoftware.com.

Thanks to the following technical expert for reviewing this article: Scott Hunter