VSM Cover Story

Generate Web Sites Automatically

Visual Studio 2008 Service Pack 1's new ASP.NET Dynamic Data templates automatically generate code for DataGrid, ListView, DetailsView, and FormView display and editing controls on dynamic pages derived from a master page and CSS stylesheet. Dynamic Data leverages AJAX to smooth page transitions and delivers scaffolding for codeless, key-based page routing with Entity Framework or LINQ to SQL object/relational mapping tools.

TECHNOLOGY TOOLBOX: VB.NET, C#, SQL Server, ASP.NET, XML

Benjamin Franklin's "Time is money" quotation qualifies as a cliché these days, but it's a truism when you're developing data-intensive Web applications. Much of Ruby on Rails' initial traction among Web developers came from its highly touted capability for generating an entire Web site from relatively few Ruby

instructions that define the underlying database schema. Rails' dynamic scaffolding feature delivers a complete set of simple model-view-controller (MVC) data-entry and editing pages automatically. Migrations enforce a schema-first design pattern and create tables from the schema automatically. ActiveRecord provides simple object/relational mapping (O/RM) features. These were three of the several features that caused Ruby on Rails to become the poster child of rapid application development (RAD) for data-backed Web sites.

The ASP.NET team intends to wrest a bit of the RAD market share from Rails with its new ASP.NET Dynamic Data feature in Visual Studio (VS) 2008 Service Pack 1. Dynamic Data derives UI behavior from the data layer's entity model and quickly scaffolds a complete, customizable Web site for data entry and editing with templated GridView and DetailsView controls. These controls include full support for navigating associated EntitySets and EntityRefs (foreign keys), as well as automated filtering. I'll start with a brief discussion of Dynamic Data's architecture and how routing navigates to pages, and continue with step-by-step instructions for generating a dynamic administrative Web application from the Northwind sample database and Entity Framework in less than five minutes. Next, I'll walk you through an alternative design approach that uses the Dynamic Data Wizard to autogenerate a set of custom static pages with templated ListView and FormView controls from a LINQ to SQL data model. I'll also explain how to customize dynamic and static pages for ordinary users by modifying the Site.master master page and Site.css CSS files, as well as add data validation, default values, and other end-user accouterments that apply to all pages. Finally, I'll describe recently added v1 capabilities, such as pluggable data providers, third-party add-ons, and coming attractions in v2, especially an MVC version.

An administrative ASP.NET Web app or site consists of dynamic or static Web forms with enhanced GridView, DetailsView, and navigation controls that enable developers or DBAs to create, retrieve, update, and delete (CRUD), as well as filter database records. Dynamic Data autogenerates code to create dynamic Web forms from a set of standard List.aspx, Insert.aspx, Edit.aspx, and Details.aspx page templates for the entire site. Dynamic Data also generates data field (property) templates for a variety of data types (see Figure 1). There's also a ListDetails.aspx page template that combines a GridView with a DetailsView on the same page for compatibility with earlier Dynamic Data previews. (The "Dynamic" prefix appended to control names by the December 2007 Dynamic Data community technology preview was dropped in subsequent previews). The alternative is static Web forms, which add templates for a ListView or FormView control on each page. The template-per-page approach increases data display flexibility at the expense of added site complexity. Dynamic Data's easy-to-use customization and data validation features let you turn an administrative site into a data display and entry application for ordinary users with minimum effort.

Dynamic routing information embedded in a page's URL determines the type of page to display -- List, Insert, Edit, or Details. The URL also specifies the EntitySet for a LinqDataSource or EntityDataSource component, which serves as the DataSource for a GridView or DetailsView control (see Figure 2). Dynamic Data shares .NET 3.5's new System.Web.Routing classes with the ASP.NET MVC feature that finally brings Rails' MVC design pattern to .NET Web apps. Here's the format and example of a URL to deliver the List.aspx page with a GridView or ListView displaying Product entities:

http://domain.com/EntitySetName/List.aspx

http://localhost:52080/Products/List.aspx

The Dynamic Data runtime detects one:many and many:one associations between EntityTypes and EntitySets, as well as Boolean data types. The runtime automatically adds a FilterRepeater for DynamicFilter dropdown lists above GridView controls (see Figure 3). Adding the "Dynamic" prefix to databound ASP.NET server controls indicates that they implement the IDynamicDataSource interface from the System.Web.Extensions namespace:

public interface IDynamicDataSource : IDataSource
{
    // Events
    event EventHandler<DynamicValidatorEventArgs> 
        Exception; 
    
    // Properties
    bool AutoGenerateWhereClause { get; set; }
    string ContextTypeName { get; set; }
    bool EnableDelete { get; set; }
    bool EnableInsert { get; set; }
    bool EnableUpdate { get; set; }
    string EntitySetName { get; set; }
    string Where { get; set; }
    ParameterCollection WhereParameters { get; }
}

All data-bound ASP.NET controls must implement this basic IDataSource interface, which IDynamicDataSource inherits:

public interface IDataSource
{
    // Events
    event EventHandler DataSourceChanged;

    // Methods
    DataSourceView GetView(string viewName);
    ICollection GetViewNames();
}

Implementing IDynamicDataSource adds object name, filtering, and CRUD management features to the data source's bindings. The only ASP.NET data sources from Microsoft that currently implement IDynamicDataSource, which qualifies them as data models for dynamic data, are the LinqDataSource and EntitiesDataSource. The System.Web.DynamicData.ModelProviders namespace includes derived EF... and DLinq... classes from abstract DataModelProvider, TableProvider, ColumnProvider, and AssociationProvider classes. The Dynamic Data runtime generates the metadata for the data model's foreign keys from the four EF... or DLinq... classes.

The DynamicDataManager class sets the data source control's context and table names, and sets AutoGenerateWhereClause=true if the Where string is empty. It also generates the column set of data-bound server controls for which it has a ControlName Adapter class. This is currently limited to DetailsView, FormView, GridView, ListView, and Repeater controls. The ControlName Adapter analyzes and displays foreign key values (such as ProductID) for a user control hyperlink with a meaningful value (such as ProductName) from the associated entity. Clicking on the link emits a routing URL with a format that specifies a single entity instance to populate an insert, edit, or details page:

http://domain.com/EntitySetName/PageType.aspx
?ForeignKeyName=SelectionValue

This example is the routing URL of the active link displayed in Figure 3, which opens a DetailsView page for Grandma's Boysenberry Spread (see Figure 4):

http://localhost:52080/Products/Details.aspx
?ProductID=6

Alternatively, primary key values can be an integral component of the URL:

http://localhost:52080/Products/Details.aspx/6
Edit.aspx and Insert.aspx pages include DynamicValidators for all editable property values. By default, the runtime validates that entries for required values are present, are formatted appropriately for their data type, and that string values don't exceed the specified maximum length specified by the table (see Figure 5). Entity metadata, which .NET 3.5's new System.ComponentModel.Da­taAnnotations classes define, lets you add regular expression and range validators. In addition, entity metadata enables you to customize a property's display name, add a description tooltip, format text values, and tweak several more characteristics. Changes you make to entity metadata apply to the EntitySet, such as Products, anywhere it or a member appears in a control. I'll show you examples of code for several metadata types after the two walkthroughs. The downloadable sample code contains C# examples for most metadata types (see Additional Resources).

Generate a Dynamic-Page Web App
You add the Dynamic Data Entities Web Application template as a new project item. This template creates dynamic-page Web applications backed by an Entity Data Model. Alternatively, the Dynamic Data Web Application template (without "Entities") uses LINQ to SQL as its data model. Start by downloading the current ASP.NET Dynamic Data preview for VS 2008 SP1 from CodePlex (see Additional Resources). The May 23, 2008, version used to write this article is a private drop of Dynamic_Data_Runtime_and_Templates_0523.zip and Preliminary_ASP.NET_Documentation.zip that you can install anywhere on your development machine. The latter archive contains a Preliminary_ASP.NET_Document-ation.chm and accompanying CHW file. This release's runtime bits and templates don't have a conventional setup program, so you must run Install.cmd from the Command Prompt to install them; the source folder includes an Uninstall.cmd script. (It's likely that a later preview or a beta with a conventional MSI installer file will be available by the time you read this. VS 2008 SP1's release version will include Dynamic Data.)

For a Web app backed by an Entity Data Model, start VS 2008 SP1 or Visual Web Developer Express SP1, and begin by choosing New, then Project. Next, select the Dynamic Data Entities Web Application template, type a project name, such as DynamicDataEntitiesWebApp, and click on OK. (If you'd rather use the Dynamic Data Wizard and LINQ to SQL to generate a static project, skip to the next topic.) The runtime creates the scaffolding framework's Dynamic Data subfolder with Content, CustomPages, FieldTemplates, and PageTemplates subfolders, as well as a Web.config file. You'll also find Default.aspx, Global.asax, Site.css, and Site.master files, which apply to all pages, in the project folder.

Right-click on the ProjectName node, choose Add, New Item, select the ADO.NET Entity Data Model from the Add New Item's Templates pane, replace the Name text box's Model1.edmx with Northwind.edmx, and click on Add to start the Entity Data Model Wizard. (See Additional Resources to download screen captures of all Entity Data Model Wizard dialogs or Additional Resources to view the dialogs online.)

With the default Generate from Database option selected in the Choose Model Contents wizard dialog, click on "next" to open the Choose Your Data Connection dialog. Select a Server or Database Explorer connection to the Northwind sample database, accept NorthwindEntities as the connection string name, and click on Next to open the Choose Your Database Objects dialog, which populates a tree view with Tables, Views, and Stored Procedures nodes. Expand the Tables node and mark the Categories, Customers, Employees, Order Details, Orders, Products, Shippers, and Suppliers tables to include in the NorthwindModel. Click on "finish" to generate the data model in the designer and its class files. A real-world data model would have singular EntityType names, plural EntitySet names, and Navigation Properties names that conform to their multiplicity (singular for 0 or 1, plural for *); this was done for the sample code.

Dynamic Data uses the Global.asax file to set RegisterRoutes parameters that apply to the project as a whole, such as the name of the ObjectContext type and whether to scaffold all pages. Open the Global.asax file, uncomment the line that starts with model.RegisterContext ..., change the YourDataContextType argument value to the namespace-qualified ObjectContext type name, NorthwindModel.NorthwindEntities, and change ScaffoldAllTables = false to ScaffoldAllTables = true. To prevent "The resource cannot be found" errors when you have a page other than Default.aspx active in the editor, open the project's Property pages, select the Web page and its Specific Page option in the Start Action group, click on the builder (...) button, and double-click on Default.aspx. Press F5 to save your changes, and click on OK to enable Web project debugging in the Debugging Not Enabled message box. Next, compile and run the project. After a few seconds, a soft-hued Default.aspx page opens with a GridView whose rows contain links to a dynamic List.aspx page for each EntitySet.

Use the Wizard to Generate Custom Pages
An alternative to the Entity Framework and dynamic pages is to use the Dynamic Data Wizard to create a Web site of custom static pages with LINQ to SQL as the data model. The wizard is scheduled for distribution some time after VS 2008 SP1's release to manufacturing (RTM); you can expect future previews to enable creating Entity Data Models with the wizard. The wizard lets you quickly create a customized site with pages that contain templated ListViews and FormViews whose property column order and other features you can customize individually -- a feature dynamic pages can't handle. It's also easier to use the Wizard to omit scaffolding for individual tables with static pages. The downsides of this approach are that the wizard doesn't autogenerate DynamicFilter dropdowns, the number of custom page files is up to four times the number of tables -- a total of 40 for Northwind without territories tables -- and, for the May 2008 preview, your database choice is SQL Server only.

The wizard adds the custom page files it creates to the CustomPages folder. The routing runtime function inspects this folder for subfolders and page names that correspond to dynamic pages, such as \Website\DynamicData\CustomPages\List.aspx. If the runtime routing function detects a corresponding custom page, it loads the static page instead of a dynamic page of the same type with the designated data model. Alternatively, you can add a conventional, hand-crafted Web page with the same name to thoroughly customize an operation. Interception makes routing operations transparent to page type.

To create a new file-system Web site with the Dynamic Data Wizard preview, choose New, Web Site, select Dynamic Data Website Wizard (Preview) in the Visual Studio Installed Templates pane, type a path for the site in the Location text box, such as C:\DynamicDataWebSite, and click on OK to start the wizard. In the wizard's Welcome dialog, accept the default Generate a New Data Model from the Database option, select an existing connection in the dropdown list, and click on Next to open the Create a Data Model from the Database dialog. (See Additional Resources to download screen captures of all Dynamic Data Wizard dialogs or Additional Resources on to view the dialogs online.)

Clear the Views, Stored Procedures, and Functions checkboxes, and then select the eight original Northwind tables, omitting those related to regions and territories. Leave the Data Context Namespace textbox empty, if it's present in later previews, and accept the default Northwind as the Data Context Class Name. Then click on Next to open the Add Data Pages to Your Website dialog with the Available Modules list filled with List, Details, Edit, and Insert nodes for each EntitySet. Unless you have a reason to do otherwise, click on Add All to populate the Website Structure list with all nodes from the Available Modules list.

Click on Next to open the Customize Your Website dialog. Checkboxes let you determine which types of pages to generate and whether deletion with a JavaScript function is permitted. With a List.aspx file such as CategoryList selected, you can change from editing with an Edit.aspx page to inserting with an Insert.aspx page by choosing Inline from the dropdown Details list. Clicking on the Edit button opens an Edit Fields dialog that lets you select which properties (columns) to display and set properties of association endpoints based on foreign keys. (This dialog wasn't fully cooked in the May preview.) Click on Finish to complete this dialog.

In the Merge Folders message box, mark the Apply to All Items check box and then click on Yes to overwrite duplicate data in several subfolders. Right-click on SolutionExplorer's Default.aspx node and choose Set as Default. Expanding SolutionExplorer's \WebSite\DynamicData\CustomPages folder exposes a subfolder for each EntitySet that contains four static ASPX files. These files duplicate the ASPX files in the PageTemplates folder. You can customize individual pages of a dynamic Web app by creating a CustomPages subfolder named for the EntitySet and adding copies of the appropriate ASPX files from the PageTemplates folder.

Press F5 to build and run the project, which opens a site that's functionally equivalent to the DynamicDataWebApp.sln project. If you open the Orders page, you'll see that the navigation properties of associated entities (EntityRefs, EntitySets, or foreign key fields) follow their order in the tables. These columns appear as the rightmost columns of GridViews or bottommost text boxes of DetailsViews when you use the Entity Data Model as the data access layer because navigation properties follow field properties in the EntityType classes.

Style with Site.CSS, Site.master, and Default.aspx
Dynamic Data's use of a master page and consistent templates for page layout and control formatting of all four page types enables changing colors, fonts, and other design elements with just a few revisions to the Site.css style sheet file. Styles are grouped by General, Tables, paging, dropdown lists, and bottom hyperlinks categories. For example, here's the original body.template style that specified about 8-point Tahoma in gray as the default font:

body.template
{
    padding-left: 8px;
    padding-right: 8px;
    font-family: Tahoma, Arial, sans-serif;
    font-size: 75%;
    color: #666666;
}

It's easy to customize this by editing the CSS. The next style replaces Tahoma with Calibri as the default body font, increases the size to about 11 points, and changes the color to black:

body.template
{
    padding-left: 8px;
    padding-right: 8px;
    font-family: Calibri, Arial, sans-serif;
    font-size: 100%;
    color: #111111;
}

Changing all fonts to Calibri or another font family requires replacing Tahoma and Trebuchet MS references in the file. You can import a new Site.css file from either of the sample projects to make the preceding changes, as well as change the small caps style to caps/lower case and pastel blue fonts to bright blue on all pages and controls.

The Site.master file for the master page contains an AJAX ScriptManager component and ASP.NET ContentPlaceHolder control, and sets the site title and <h1> header text, which you should change from the generic "Dynamic Data Site." Optionally, you can remove the <span class="allcaps"> style from <h1>. Default.aspx contains a ScriptManagerProxy, as do all templates, to enable the page to define its own AJAX behavior, the <h2> header text, which also needs changing from "My tables," and the GridView for the EntitySets list. PageTemplates have AJAX UpdatePanels, which enable partial page rendering in conjunction with the ScriptManagerProxy. Partial rendering improves the user experience by changing only altered page regions on postback. When debugging AJAX client behavior, the Dynamic Data team recommends that you disable partial rendering temporarily to receive meaningful error messages. Do this by setting ScriptManager's EnablePartialRendering attribute to "false" on the Site.master page.

Format and Validate with Entity Metadata
Entity metadata is the key to customizing data presentation and validation. As I mentioned earlier, the System.ComponentModel.DataAnnotations namespace defines the 14 different types of annotations for classes that specify entity types (see Additional Resources, to access a table that lists custom validation attributes: Range, RegularExpression, Required, and StringLength). Assigning DataAnnotations as a member of System.ComponentModel -- rather than System.Web.DynamicData -- makes them available to other .NET-supported UIs in addition to Web forms.

The Dynamic Data runtime automatically extracts metadata for validating required properties and maximum property value size from the data model. The runtime also identifies association endpoints. However, there are many other data formatting nuances that aren't easily represented by CLR types, such as DateTime, currency, percentage, and special strings that represent email addresses, URLs, passwords, and so on. You can use one of the DataTypeAttribute values to format the field appropriately or override the format with the DisplayFormat attribute. This example comes from the sample project's partial Employee class in the CustomMetadata.cs file:

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

public class EmployeeMetadata
{
    [DataType(DataType.Date)]
    [DisplayName("Birth Date")]
    [Required(ErrorMessage = 
        "The Birth Date field is required")]
    public object BirthDate { get; set; }

	// Alternate DateTime formatting
    [DisplayFormat(DataFormatString = "{0:d}")]
    [DisplayName("Hire Date")]
    [Required(ErrorMessage = 
        "The Hire Date field is required")]
    public object HireDate { get; set; }

    // Hide Photo field
    [ScaffoldColumn(false)]
    public string Photo { get; set; }

    // Hide PhotoPath field
    [ScaffoldColumn(false)]
    public string PhotoPath { get; set; }

    // Hide Employees (self-join) end
    [ScaffoldColumn(false)]
    public string Employees { get; set; }

    // Rename Employee1 (self-join) end
    [DisplayName("ReportsTo")]
    public string Employee1 { get; set; }

}

.NET 3.5 doesn't let you add metadata to existing partial classes; for dynamic pages, you must declare a secondary class to be of the EntityName Metadata type and then provide the corresponding type definition class.

The CustomMetadata.cs file of both sample projects contains examples of RegularExpression validation of dates (with optional time), Range validation of percentage, and percent-data formatting with DisplayFormat. Custom (static) pages you create with the Dynamic Data Wizard generate a complete DataContextName PartialClass.cs or .vb file, so you can apply attributes to its classes and properties instead of creating your own custom metadata partial class file. If you've created a custom metadata file for a dynamic Web app, you can remove the DataContextName PartialClass file from a static Web site project and substitute the custom file. Remove the Namespace declaration and conform the EntityType names if you didn't singularize them.

Dynamic Data's Past and Future
Web sites created by autogenerating pages suffer from an undeserved "quick and dirty" stigma because they dramatically reduce the number of developer hours required to get a data-driven Web site up and running. The first automated Web site-generation example from Microsoft was Blinq, a command-line project that Polita Paulus developed and Scott Guthrie demonstrated in a Tech·Ed 2006 session (see Additional Resources, which begin at the bottom of this page). Many Blinq enthusiasts were disappointed that the ASP.NET team didn't upgrade Blinq for .NET 3.5 and VS 2008 with a customization API. Instead, a Blinq work-alike that adopted IronPython as its programming language arrived as an incubator project named "Jasper" at MIX07. The overview for the Jasper: MIX07 CTP download claimed that "Jasper leverages the power of dynamic languages and the concept of convention over configuration to provide a programming surface for data that enables rapid development of data-bound applications" [emphasis added]. An insinuation that Ruby on Rails was suitable only for "working against simple databases" followed. Needless to say, this statement stirred up a storm of rebuttals by Rails developers.

Project "Astoria" also emerged at MIX07. Jasper languished as a result of developer indifference, but Astoria emerged as RESTful ADO.NET Data Services. Microsoft's Andrew Conrad stated in a May 2007 blog post that Blinq "actually morphed into Dynamic Data Controls for ASP.NET," which was then part of the Microsoft ASP.NET Futures CTP. Although Dynamic Data is quick as a RAD tool, it certainly isn't dirty. According to Scott Hunter, a Dynamic Data program manager, scaffolded sites perform within 10 percent of hand-crafted equivalents (see Additional Resources). On the other hand, Dynamic Data doesn't support domain-driven design or enforce separation of concerns. But tight coupling to today's choices of data models is the primary source of these issues. More serious drawbacks are that Dynamic Data isn't suited to test-driven development (TDD), and it lacks a built-in authentication and authorization system.

On the plus side, Dynamic Data now has a "pluggable" data model API to support additional LINQ-compliant providers, such as ADO.NET Data Services. LLBLGen Pro was the first non-Microsoft data model to demonstrate compatibility with the DynamicDataManager, and Hunter mentioned that the team is hoping to get LINQ-enabled NHibernate as a data model. U.K. developer Steven Naughton has published code for a complete role-based permissions system (see Additional Resources).

Hunter said that future work for Dynamic Data v2 includes adding a unit-testable MVC version and "removing the negatives" from data-bound controls, such as the amount of ViewState they generate, which the team will address by attempting to run with ViewState off. The team also plans to make v2 data controls support the Entity Data Model's many:many associations, flatten "non-rectangular data," and support complex (value) types. You can preview some v2 features by downloading the latest Dynamic Data Futures release from CodePlex (see Additional Resources). But there's no need to wait for v2 to check out Dynamic Data; give the v1 preview a quick test drive to see if it meets your requirement for RAD Web apps or sites.

comments powered by Disqus
Upcoming Events

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.