Chapter 1. Getting Started

In This Chapter

Creating a New Dynamic Data Application

Creating a Data Model

Metadata in Entity Framework and Dynamic Data

Field Templates and Dynamic Controls

Data Annotations

Entity Templates

Grid Columns and Dynamic Fields

Filter Templates

Page Templates and URL Routing

Dynamic Data is an extension of the ASP.NET WebForms framework. It includes a number of new controls for rendering common UI elements dynamically, including data entry fields, forms, lists, and search criteria. These controls complement the standard ASP.NET controls—GridView, FormView, DetailsView, and ListView and serve as building blocks for creating new or improving existing web pages.

Dynamic controls are not a black box. They rely on templates included in the Web projects and can be customized by developers. At run time, the dynamic controls rely on metadata to locate and render the templates appropriate for a particular model element. For example, when rendering a data entry control for a foreign key column, Dynamic Data chooses a template with a drop-down list. However, when rendering control for a string column, it chooses a template with a text box.

Dynamic Data supports a number of existing LINQ-based data models, including LINQ to SQL and Entity Framework. In addition to the powerful and easy-to-use data access functionality, these frameworks also provide rich metadata APIs that get you detailed information about tables in the data model, including their names, attributes, data types, and relationships. Because these APIs are different, Dynamic Data provides its own metadata API that hides the inconsistencies and exposes information most relevant to user interface development. This approach helps to keep the user interface code largely independent from the underlying data access framework. With the exception of the many-to-many field template, unique to the Entity Framework, most of the dynamic templates work equally well with both LINQ to SQL and Entity Framework, allowing developers to reuse the templates between different web applications or change the data access framework without rewriting the presentation logic.

In addition to the data entry controls, Dynamic Data also takes advantage of the URL routing to generate entire websites based on the metadata exposed by the data model and the page templates defined by the developer. Out of the box, Dynamic Data web applications already support common operations such as creating a new entity, displaying a list of entities based on search criteria, or changing or deleting an existing entity. These pages are fully functional and integrated in the application; they can handle different column types and include validation and error handling logic.

Dynamic Data is backward compatible with non-LINQ-based data access frameworks, such as the traditional ADO.NET with DataSets, as well as custom business objects. With very little effort, developers can extend existing WebForms applications to take advantage of the rich field templating capabilities. Unfortunately, page templating and dynamic search capabilities are currently supported only with LINQ-based frameworks and discussion of Dynamic Data without them would not be complete.


Note

Although it shares many common elements with the ASP.NET MVC framework, including LINQ-based data models and data annotation attributes, Dynamic Data has several distinct advantages. It works with existing WebForms applications and does not require web developers to introduce a completely new presentation framework. Unlike MVC, it supports dynamic rendering of UI elements not only at the field level, but also at the entity and page levels, which can significantly reduce the amount of code developers have to write and maintain. Moreover, Dynamic Data’s ability to generate complex LINQ queries at runtime, based on the search criteria entered by the user, has no direct equivalent in MVC.


Dynamic Data is ideal for implementing maintenance and administration pages in web applications as it supports most of the functionality they require out of the box. However, the biggest advantage Dynamic Data offers is a higher degree of code reuse. The resulting consistency of user interface across different screens and entities is especially important in customer-facing pages of web applications.

Creating a New Dynamic Data Application

Visual Studio 2010 includes two different flavors of project templates for Dynamic Data:

• ASP.NET Dynamic Data Entities

• ASP.NET Dynamic Data LINQ to SQL

Figure 1.1 shows how these templates appear in the New Web Site dialog of Visual Studio. These project templates are nearly identical, and you should choose one based on the data access technology you want to use. If your project, like the sample project accompanying this book, uses Entity Framework, choose the ASP.NET Dynamic Data Entities project flavor.

Image

Figure 1.1. Dynamic Data website templates.

You can choose either Visual Basic or Visual C# flavors of the Dynamic Data project templates depending on your language preference. Aside from Visual Basic being slightly more verbose than C# when it comes to defining lambda expressions, both languages work equally well.

In Visual Studio, you also have a choice of creating either a Web Application project or a Web Site. A Web Site is simply a folder that contains page (.aspx), control (.ascx), and code-behind files. This type of Visual Studio project is appropriate for web applications where content is stored primarily in the page files, as it enables developers to change and deploy one page at a time. A Web Application project, on the other hand, is more appropriate for applications where content is stored primarily in a database and the page files contain only the logic required to retrieve and display it.

Although Dynamic Data offers both Web Site and Web Application project templates, its intent fits closer with the Web Application project model. Figure 1.2 shows the Dynamic Data project templates in the New Project dialog of Visual Studio. The sample project accompanying this book is based on the ASP.NET Dynamic Data Entities Web Application project template.

Image

Figure 1.2. Dynamic Data Web Application project templates.

Figure 1.3 shows an example of a new Dynamic Data web application project, which as you can see, is similar to a regular Web Application project; it includes a default master page (Site.master), a cascading style sheet (Site.css), and a landing page (Default.aspx). However, it is not quite ready to run.

Image

Figure 1.3. Dynamic Data Web Application project.

Creating a Data Model

Your first task after creating a new Dynamic Data project is to create a data model. Because the sample project in this book is based on Entity Framework, you would choose the ADO.NET Entity Data Model template from the Data folder in the Add New Item dialog (see Figure 1.4).

Image

Figure 1.4. Data model templates.

The data model includes some or all tables in a single database, so when naming the data model file, it is a good idea to use the name of the database it will represent. In this book, Northwind sample database is used, and the entity data model file is Northwind.edmx.


Note

The Northwind sample database for SQL Server is available for download from Microsoft at the following address:

http://www.microsoft.com/downloads/en/details.aspx?FamilyID=06616212-0356-46a0-8da2-eebc53a68034

Originally developed for SQL Server 2000, it works with versions 2005 and 2008.

The installation program, SQL2000SampleDb.msi, extracts the database files and scripts in the “C:SQL Server 2000 Sample Databases” directory on your computer. You can create the Northwind database by running the following commands in the Command Prompt window:

c:
cd "SQL Server 2000 Sample Databases"
sqlcmd -S .sqlexpress -E -i instnwnd.sql

Note that in this example, -S .sqlexpress refers to a local installation of the Express edition of SQL Server. If you have a default installation of the Developer or another edition on your computer, you need to change this parameter to . or specify the actual name your SQL Server.


When a new entity data model is added to the project, Visual Studio automatically opens the Entity Data Model Wizard that guides you through the process of creating the model. You can choose to create an empty data model, where you manually define new entities, specify their properties and associations, and then map these definitions to the storage definitions imported from a database later. However, in this example, you choose the option to generate the model from the database. After prompting you to enter the connection string, the wizard displays the page shown in Figure 1.5 for you to select the database tables, views, and stored procedures to be included in the model.

Image

Figure 1.5. Entity Data Model Wizard.

In this example, you want to include all database tables in the model. The resulting Northwind entity data model, after some cleanup to improve readability, is shown in Figure 1.6.

Image

Figure 1.6. Northwind data model.

Entity Framework allows you to write strongly typed database queries using C# and Visual Basic code as opposed to traditional T-SQL statements embedded in string constants or literals. Consider an ASP.NET page in Listing 1.1 that displays database records in a GridView control.

Listing 1.1. Entity Framework Sample (Markup)


<%@ Page Language="C#"
  MasterPageFile="~/Site.master" CodeBehind="SamplePage.aspx.cs"
  Inherits="WebApplication.Samples.Ch01.AdoNet.SamplePage" %>

<asp:Content ContentPlaceHolderID="main" runat="server">
  <asp:GridView ID="gridView" runat="server" />
</asp:Content>


Aside from relying on Site.master, the default master page provided by the Dynamic Data project template, this page is very simple and has only one GridView control.

In the code-behind shown in Listing 1.2, the NorthwindEntities class is generated by Visual Studio based on the information in the Northwind.edmx in this project. This class encapsulates the database connection and allows you to use LINQ (language-integrated query) to select records from the Customers table. LINQ resembles the traditional SQL syntax to which many database developers are accustomed. It is usually a little more compact, but more importantly, it is safer and easier to maintain. The Entity Framework automatically generates parameterized SQL queries, eliminating SQL injection vulnerabilities, and allows the compiler to check the query correctness and find typos in table and column names that would otherwise go unnoticed until the query is executed at run time.

The resulting web page is shown in Figure 1.7.

Image

Figure 1.7. GridView with customer records from the Northwind Database.

Listing 1.2. Entity Framework Sample (Code-Behind)


using System;
using System.Linq;
using DataModel;

namespace WebApplication.Samples.Ch01.EntityFramework
{
  public partial class SamplePage : System.Web.UI.Page
  {
    protected void Page_Load(object sender, EventArgs e)
    {
      using (NorthwindEntities context = new NorthwindEntities())
      {
        var query = from c in context.Customers
                    select c;

        this.gridView.DataSource = query.ToList();
        this.gridView.DataBind();
      }
    }
  }
}


ASP.NET also provides a data source control designed to work with Entity Framework. Data source controls encapsulate the database operations needed by typical CRUD screens and allow you to implement them declaratively, using markup instead of code. By changing the previous example to use the EntityDataSource, you eliminate the code-behind file while keeping functionality of the page intact.

Listing 1.3. EntityDataSource Sample (Markup)


<%@ Page Language="C#" MasterPageFile="~/Site.master" %>

<asp:Content ContentPlaceHolderID="main" runat="server">
  <asp:GridView ID="gridView" DataSourceID="dataSource" runat="server" />
  <asp:EntityDataSource ID="dataSource" runat="server"
    ConnectionString="name=NorthwindEntities"
    DefaultContainerName="NorthwindEntities" EntitySetName="Customers" />
</asp:Content>


The page in Listing 1.3 has an EntityDataSource control configured to access the Customers table from the NorthwindEntities data model. The GridView control is now data-bound declaratively, using the DataSourceID attribute, which contains the ID of the EntityDataSource control.

Metadata in Entity Framework and Dynamic Data

The generated NorthwindEntities class inherits from the ObjectContext base class provided by Entity Framework. This class, as well as its LINQ to SQL counterpart, DataContext, provides access to rich metadata describing all tables in the data model, including their names, columns, data types, associations, and more. Although both LINQ to SQL and Entity Framework offer similar types of metadata, they use different APIs to represent it. Dynamic Data encapsulates these differences, provides a single metadata API that works for both of these frameworks, and extends it with presentation metadata, such as display labels and validation error messages.

Rich metadata API enables Dynamic Data web applications to generate user interface elements such as data entry fields, forms, and entire pages at runtime, all based on the definitions derived from the database and extended by the developer. However, before the web application can access this metadata, the data context class needs to be registered in the application Global.asax file as in Listing 1.4.

Listing 1.4. Registering Data Model with Dynamic Data in Global.asax.cs


using System;
using System.Data.Entity;
using System.Web.DynamicData;
using DataModel;

namespace WebApplication
{
  public class Global : System.Web.HttpApplication
  {
    public static readonly MetaModel DefaultModel = new MetaModel();

    void Application_Start(object sender, EventArgs e)
    {
      DefaultModel.RegisterContext(typeof(NorthwindEntities));
    }
  }
}


Field Templates and Dynamic Controls

Dynamic Data field templates take advantage of the metadata and make it easier to implement data entry controls and validation logic for individual fields or columns in tables exposed by the data model. Listing 1.5 shows an example of a simple edit page implemented using field templates.

Listing 1.5. DynamicControl and Field Template Sample (Markup)


<%@ Page Language="C#"
  MasterPageFile="~/Site.master" CodeBehind="SamplePage.aspx.cs"
  Inherits="WebApplication.Samples.Ch01.FieldTemplate.SamplePage" %>

<asp:Content ContentPlaceHolderID="main" runat="server">
  <asp:ValidationSummary runat="server" />
  <asp:FormView ID="formView" runat="server" DataSourceID="dataSource"
    DataKeyNames="ProductID" DefaultMode="Edit">
    <EditItemTemplate>
      Product Name:
      <asp:DynamicControl DataField="ProductName" Mode="Edit" runat="server" />
      <br />
      Units in Stock:
      <asp:DynamicControl DataField="UnitsInStock" Mode="Edit" runat="server" />
      <br />
      <asp:LinkButton Text="Update" CommandName="Update" runat="server" />
    </EditItemTemplate>
  </asp:FormView>
  <asp:EntityDataSource ID="dataSource" runat="server"
    ConnectionString="name=NorthwindEntities" EnableUpdate="true"
    DefaultContainerName="NorthwindEntities" EntitySetName="Products" />
</asp:Content>


DynamicControl is a new ASP.NET control provided by Dynamic Data. Based on the MetaTable associated with its FormView, as well as its own DataField and Mode properties, this control selects an appropriate field template from the FieldTemplates folder of the Dynamic Data project shown in Figure 1.8. For the ProductName and UnitsInStock fields in this example, the dynamic controls will select the Text_Edit.ascx and Integer_Edit.ascx templates, respectively.

Image

Figure 1.8. Dynamic Data field templates.

The last bit of magic this page needs is a Page_Init event handler in the code-behind to associate a MetaTable object describing the Products table with the FormView control, as shown in Listing 1.6.

Listing 1.6. DynamicControl and Field Template Sample (Code-Behind)


using System;
using System.Web.DynamicData;

namespace WebApplication.Samples.Ch01.FieldTemplate
{
  public partial class SamplePage : System.Web.UI.Page
  {
    protected void Page_Init(object sender, EventArgs e)
    {
      MetaTable table = Global.DefaultModel.GetTable(this.dataSource.EntitySetName);
      this.formView.SetMetaTable(table);
    }
  }
}


The resulting web page rendered at runtime is shown in Figure 1.9.

Image

Figure 1.9. Data entry form with field templates.

Field templates are nothing more than a special type of user controls, which should be familiar to most WebForms developers. Listing 1.7 shows the Integer_Edit field template after a minor cleanup to improve readability.

Listing 1.7. Integer_Edit Field Template (Markup)


<%@ Control Language="C#" CodeBehind="Integer_Edit.ascx.cs"
  Inherits="WebApplication.DynamicData.FieldTemplates.Integer_EditField" %>

<asp:TextBox ID="textBox" runat="server" Text="<%# FieldValueEditString %>" />

<asp:RequiredFieldValidator runat="server" ID="requiredFieldValidator"
  ControlToValidate="textBox" Enabled="false" />
<asp:CompareValidator runat="server" ID="compareValidator"
  ControlToValidate="textBox" Operator="DataTypeCheck" Type="Integer" />
<asp:RegularExpressionValidator runat="server" ID="regularExpressionValidator"
  ControlToValidate="textBox" Enabled="false" />
<asp:RangeValidator runat="server" ID="rangeValidator"
  ControlToValidate="textBox" Type="Integer" Enabled="false"
  EnableClientScript="true" MinimumValue="0" MaximumValue="100" />
<asp:DynamicValidator runat="server" ID="dynamicValidator"
  ControlToValidate="textBox" Display="Static" />


As you can see, it has a TextBox control for entering the field value and a CompareValidator to prevent users from typing alphabetic symbols. This is similar to the markup you would normally write by hand to validate the UnitsInStock values. However, in the field template, all controls are initialized in the code-behind shown in Listing 1.8 based on the metadata information describing the column.

Listing 1.8. Integer_Edit Field Template (Code-behind)


using System;
using System.Collections.Specialized;
using System.Web.DynamicData;
using System.Web.UI;

namespace WebApplication.DynamicData.FieldTemplates
{
  public partial class Integer_EditField : FieldTemplateUserControl
  {
    public override Control DataControl
    {
      get { return this.textBox; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
      this.textBox.ToolTip = this.Column.Description;

      this.SetUpValidator(this.requiredFieldValidator);
      this.SetUpValidator(this.compareValidator);
      this.SetUpValidator(this.regularExpressionValidator);
      this.SetUpValidator(this.rangeValidator);
      this.SetUpValidator(this.dynamicValidator);
    }

    protected override void ExtractValues(IOrderedDictionary dictionary)
    {
      dictionary[this.Column.Name] = this.ConvertEditedValue(this.textBox.Text);
    }
  }
}


Field templates are incredibly flexible. This particular template, Integer_Edit, can be reused for other integer columns in the Product table, such as UnitsOnOrder and ReorderLevel, as well as integer columns in all other tables in the data model. The field template will even configure the RangeValidator based on the range of values appropriate for a data type of the particular column, such as Int16 or Int32. All you need to do is place a DynamicControl on your page and specify its DataField and Mode properties.

Data Annotations

The data entry page created using dynamic controls has one significant difference compared to a page that uses traditional text boxes and validators. Notice the error message shown in Figure 1.10 includes the actual column name, UnitsInStock, as opposed to something like “Units in Stock” that you might expect to see in a user-friendly application. This is because the DynamicControl cannot infer the display name from the original column name. In this particular case, you could use capitalization of letters to split the column name into individual words, but in general, Dynamic Data cannot rely on this being the case. Your column names could use underscores, abbreviations, acronyms, or numbers that would be impossible for the framework to convert to readable English, let alone other languages you might need to support in your application.

Image

Figure 1.10. Dynamic data entry page with validation errors.

In traditional ASP.NET code, you would have specified the error message in page markup, using the ErrorMessage attribute of the CompareValidator control. However, in the Dynamic Data example, the CompareValidator resides in the Integer_Edit field template that can be reused for all integer columns in the data model. You cannot simply hard-code the “Units in Stock” error message there because it would make error messages for all other columns wrong. Instead, you need to extend the data model and specify a human-readable display name for the UnitsInStock column that will be used in labels and error messages presented to users.

At this time, neither Entity Framework nor LINQ to SQL allow you to specify display names for columns in the graphical designer. Instead, you need to use data annotations and extend the model in code. Data annotations are .NET attributes defined in the System.Component.DataAnnotations namespace. They can be applied to entity classes and their properties to extend table and column metadata information used by Dynamic Data. Listing 1.9 shows an example of using the DisplayAttribute to specify a human-readable display name for the UnitsInStock column of the Product table.

Listing 1.9. Extending Product Metadata Using Data Annotations


using System;
using System.ComponentModel.DataAnnotations;

namespace DataModel
{
  [MetadataType(typeof(Product.Metadata))]
  partial class Product
  {
    public abstract class Metadata
    {
      [Display(Name = "Units in Stock")]
      public object UnitsInStock { get; set; }
    }
  }
}


Notice that the Product class is declared with the partial keyword. This effectively extends the definition of the Product entity class generated by the Entity Framework designer, making everything in this code snippet a part of the generated Product class. This is necessary because you cannot place the DisplayAttribute directly on the generated UnitsInStock property. Any changes made in the generated code by hand will be lost the next time the code is regenerated. Instead, you define a separate class (called Metadata in this example) and use the MetadataTypeAttribute to associate it with the entity class, Product. Any data annotation attributes placed on the properties of the Metadata class will have the same effect as if they were placed on the actual properties of the Product class, such as the UnitsInStock in this example. After making these changes, the error message displayed by the Integer_Edit field template when the user enters alphabetic symbols in the UnitsInStock field will include the human-readable display name.

Entity Templates

Metadata used by Dynamic Data includes not only information about individual columns, but also the information about entire tables. If you need to build a data entry page that includes all fields of the Products table, you can replace all individual DynamicControl instances with a single DynamicEntity control.

Listing 1.10. DynamicEntity and Entity Template Sample (Markup)


<%@ Page Language="C#"
  MasterPageFile="~/Site.master" CodeBehind="SamplePage.aspx.cs"
  Inherits="WebApplication.Samples.Ch01.EntityTemplate.SamplePage" %>

<asp:Content runat="server" ContentPlaceHolderID="main">
  <asp:ValidationSummary runat="server" />
  <asp:FormView ID="formView" runat="server" DataSourceID="dataSource"
    DataKeyNames="ProductID" DefaultMode="Edit">
    <EditItemTemplate>
      <asp:DynamicEntity Mode="Edit" runat="server" />
      <tr><td>
        <asp:LinkButton Text="Update" CommandName="Update" runat="server" />
      </td></tr>
    </EditItemTemplate>
  </asp:FormView>
  <asp:EntityDataSource ID="dataSource" runat="server"
    ConnectionString="name=NorthwindEntities" EnableUpdate="true"
    DefaultContainerName="NorthwindEntities" EntitySetName="Products" />
</asp:Content>


DynamicEntity is another control provided by Dynamic Data. Based on the table associated with the FormView control to which it belongs as well as its own Mode property, the control will choose and load an appropriate entity template. With the Products table in this example, the resulting web page will look similar to the one shown in Figure 1.11.

Image

Figure 1.11. Dynamic Data entry page with entity template.

Out of the box, Dynamic Data project templates offer three default entity templates that implement a simple, single-column, top-down form layout. These entity templates (see Figure 1.12) are used for all types of entities by default unless the developer provides a custom template for a particular entity.

Image

Figure 1.12. Entity templates in Dynamic Data project.

As you might expect, entity templates are also a special type of ASP.NET user control. Entity templates are located in the DynamicData/EntityTemplates folder of the web project. Listing 1.11 shows code from the Default_Edit entity template.

Listing 1.11. Default_Edit Entity Template (Markup)


<%@ Control Language="C#" CodeBehind="Default_Edit.ascx.cs"
  Inherits="WebApplication.DynamicData.EntityTemplates.Default_EditEntityTemplate"%>

<asp:EntityTemplate runat="server" ID="entityTemplate">
  <itemtemplate>
    <tr class="td">
      <td class="DDLightHeader">
        <asp:Label runat="server" OnInit="Label_Init" />
      </td>
      <td>
        <asp:DynamicControl runat="server" Mode="Edit"
          OnInit="DynamicControl_Init" />
      </td>
    </tr>
  </itemtemplate>
</asp:EntityTemplate>


Inside of this user control, you have an EntityTemplate control, similar to the Repeater control in ASP.NET WebForms. Its purpose is to provide a markup template with a Label and a DynamicControl that will be rendered for each column in the table. Listing 1.12 shows the code-behind file where all this happens.

Listing 1.12. Default_Edit Entity Template (Code-Behind)


using System;
using System.Web.DynamicData;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication.DynamicData.EntityTemplates
{
  public partial class Default_EditEntityTemplate : EntityTemplateUserControl
  {
    private MetaColumn currentColumn;

    protected override void OnLoad(EventArgs e)
    {
      IEnumerable<MetaColumn> scaffoldColumns = this.Table.GetScaffoldColumns(
        this.Mode, this.ContainerType);
      foreach (MetaColumn column in scaffoldColumns)
      {
        this.currentColumn = column;
        Control namingContainer = new DefaultEntityTemplate._NamingContainer();
        this.entityTemplate.ItemTemplate.InstantiateIn(namingContainer);
        this.entityTemplate.Controls.Add(namingContainer);
      }
    }

    protected void Label_Init(object sender, EventArgs e)
    {
      ((Label)sender).Text = this.currentColumn.DisplayName;
    }

    protected void DynamicControl_Init(object sender, EventArgs e)
    {
      ((DynamicControl)sender).DataField = this.currentColumn.Name;
    }
  }
}


As you can see, the control overrides the OnLoad method, retrieves the list of metadata columns from the table and instantiates the EntityTemplate inside the foreach loop for every column. The Label_Init event handler initializes label’s text with the display name of the column and the DynamicControl_Init event handler initializes the dynamic control with the name of the column itself. The rest is done by the DynamicControl instances, which load and render the appropriate field templates based on the column metadata.

Some visual aspects of entity templates, such as display names of the columns and their order, can be controlled using metadata attributes. However, the overall layout of the default template is fundamentally a single column of labels on the left and the data entry controls on the right. When this layout is insufficient, you can create custom entity templates to fit your requirements.

Grid Columns and Dynamic Fields

So far, only data entry forms have been discussed, where a single record or entity is displayed at a time. However, Dynamic Data offers great productivity enhancements for list type pages that display multiple records.

Listing 1.13. GridView and DynamicField Sample (Markup)


<%@ Page Language="C#"
  MasterPageFile="~/Site.master" CodeBehind="SamplePage.aspx.cs"
  Inherits="WebApplication.Samples.Ch01.DynamicField.SamplePage" %>

<asp:Content ContentPlaceHolderID="main" runat="server">
  <asp:ValidationSummary runat="server"/>
  <asp:GridView ID="gridView" runat="server" DataSourceID="dataSource"
    DataKeyNames="ProductID" AutoGenerateEditButton="true"
    AllowPaging="true" AutoGenerateColumns="false" >
    <columns>
      <asp:DynamicField DataField="ProductName" />
      <asp:DynamicField DataField="UnitsInStock" />
    </columns>
  </asp:GridView>
  <asp:EntityDataSource ID="dataSource" runat="server"
    ConnectionString="name=NorthwindEntities" EnableUpdate="true"
    DefaultContainerName="NorthwindEntities" EntitySetName="Products" />
</asp:Content>


DynamicField is a data control field that can be used with both GridView and DetailsView controls, allowing them to take advantage of the rich field templating functionality offered by Dynamic Data. Figure 1.13 shows how this page looks at runtime.

Image

Figure 1.13. GridView page with dynamic field columns.

Similar to DynamicControl, DynamicField uses column metadata to automatically select and load an appropriate field template. In other words, pages built with FormView, DetailsView, and GridView controls can reuse the same set of field templates.

Like the DynamicEntity control, which allows you to build a data entry form that includes all columns of a particular table, Dynamic Data offers a similar capability for grid columns. In Listing 1.14, the explicit definitions of fields for ProductName and UnitsInStock have been removed. Instead, the AutoGenerateColumns property of the GridView control is set to true.

Listing 1.14. DefaultAutoFieldGenerator Sample (Markup)


<%@ Page Language="C#"
  MasterPageFile="~/Site.master" CodeBehind="SamplePage.aspx.cs"
  Inherits="WebApplication.Samples.Ch01.FieldGenerator.SamplePage" %>

<asp:Content ContentPlaceHolderID="main" runat="server">
  <asp:ValidationSummary ID="ValidationSummary1" runat="server"/>
  <asp:GridView ID="gridView" runat="server" DataSourceID="dataSource"
    DataKeyNames="ProductID" AutoGenerateEditButton="true"
    AllowPaging="true" AutoGenerateColumns="true"/>
  <asp:EntityDataSource ID="dataSource" runat="server"
    ConnectionString="name=NorthwindEntities" EnableUpdate="true"
    DefaultContainerName="NorthwindEntities" EntitySetName="Products" />
</asp:Content>


By default, the GridView control generates a BoundField for every column in the table. Instead, you want to replace its columns generator with the one provided by Dynamic Data as shown in Listing 1.15.

Listing 1.15. DefaultAutoFieldGenerator Sample (Code-Behind)


using System;
using System.Web.DynamicData;

namespace WebApplication.Samples.Ch01.FieldGenerator
{
  public partial class SamplePage : System.Web.UI.Page
  {
    protected void Page_Init(object sender, EventArgs e)
    {
      MetaTable table = Global.DefaultModel.GetTable(this.dataSource.EntitySetName);
      this.gridView.SetMetaTable(table);
      this.gridView.ColumnsGenerator = new DefaultAutoFieldGenerator(table);
    }
  }
}


Note that DefaultAutoFieldGenerator class, despite of its somewhat misleading name, is actually provided by Dynamic Data. Based on columns of the specified MetaTable, it generates DynamicField instances for a GridView or DetailsView control that use it. Figure 1.14 shows the resulting page based on this code. Notice that the UnitsInStock column is using the Integer_Edit field template.

Image

Figure 1.14. GridView with Dynamic Field Generator.

Filter Templates

In a real-world application, the number of products would probably exceed the mere eight pages in the Northwind sample. Thus, no application would be complete without some form of search functionality. Consider Listing 1.16, where the grid page has been extended from the previous example with a DynamicFilter and a QueryExtender control.

Listing 1.16. QueryExtender and DynamicFilter Sample (Markup)


<%@ Page Language="C#"
  MasterPageFile="~/Site.master" CodeBehind="SamplePage.aspx.cs"
  Inherits="WebApplication.Samples.Ch01.DynamicFilter.SamplePage" %>

<asp:Content ContentPlaceHolderID="main" runat="server">
  Filter by Category:
  <asp:DynamicFilter ID="categoryFilter" DataField="Category" runat="server" />
  <asp:ValidationSummary runat="server"/>
  <asp:GridView ID="gridView" runat="server" DataSourceID="dataSource"
    DataKeyNames="ProductID" AutoGenerateEditButton="true"
    AllowPaging="true" AutoGenerateColumns="true"/>
  <asp:EntityDataSource ID="dataSource" runat="server"
    ConnectionString="name=NorthwindEntities" EnableUpdate="true"
    DefaultContainerName="NorthwindEntities" EntitySetName="Products" />
  <asp:QueryExtender runat="server" TargetControlID="dataSource">
    <asp:DynamicFilterExpression ControlID="categoryFilter" />
  </asp:QueryExtender>
</asp:Content>


QueryExtender is a special nonvisual control. Using the TargetControlID property, it is associated with a LINQ-based data source control, such as the EntityDataSource in this example or LinqDataSource provided by LINQ to SQL. At run time, the QueryExtender can modify the LINQ query constructed by the data source control before it is converted to SQL and sent to the database server for execution. QueryExtender uses one or more data source expressions. In this example, there is a DynamicFilterExpression associated with a DynamicFilter control at the top of the page. Before going too much deeper, look at Figure 1.15, which shows the resulting page at run time.

Image

Figure 1.15. QueryExtender and DynamicFilter Sample.

DynamicFilter is a special control provided by Dynamic Data. Based on the column name specified in its DataField property and the metadata information, it selects and renders an appropriate filter template where the user can specify search criteria for this column. Figure 1.16 shows the filter templates available in Dynamic Data projects out of the box.

Image

Figure 1.16. Filter templates in a Dynamic Data project.

It comes as no surprise that filter templates are also a special kind of ASP.NET user controls located in the DynamicData/Filters folder of the web application project. In this example, there is a filter based on the Category column of the Products table. This column is a foreign key pointing to the Categories table of the Northwind database. The default filter provided by Dynamic Data for a foreign key column is a drop-down list that displays all records from the table it references. The markup from ForeignKey.ascx is shown in Listing 1.17.

Listing 1.17. ForeignKey Filter Template (Markup)


<%@ Control Language="C#" CodeBehind="ForeignKey.ascx.cs"
  Inherits="WebApplication.DynamicData.Filters.ForeignKeyFilter" %>

<asp:DropDownList runat="server" ID="dropDownList" AutoPostBack="True"
  OnSelectedIndexChanged="DropDownList_SelectedIndexChanged">
  <asp:ListItem Text="All" Value="" />
</asp:DropDownList>


This looks deceptively simple, and as you would expect, the heavy lifting is done in code, by dynamically loading the list of database records to populate the drop-down list and constructing a lambda expression that represents an additional where clause for the LINQ query. You will not be looking at the code-behind of the filter templates just yet. Filtering functionality is by far the most complex code in Dynamic Data; the detailed discussion of this topic is found in Chapter 5, “Filter Templates.”

The QueryExtender control supports multiple filter expressions. If you need to allow users to search products by Supplier, you can simply add a new DynamicFilter control to the form and a new DynamicFilterExpression to the QueryExtender. However, if you need to have dynamic filters for all supported columns in the table, you can do it more quickly with the help of the QueryableFilterRepeater control, as shown in Listing 1.18.

Listing 1.18. QueryExtender and QueryableFilterRepeater Sample (Markup)


<%@ Page Language="C#"
  MasterPageFile="~/Site.master" CodeBehind="SamplePage.aspx.cs"
  Inherits="WebApplication.Samples.Ch01.FilterRepeater.SamplePage" %>

<asp:Content ContentPlaceHolderID="main" runat="server">
  <asp:QueryableFilterRepeater runat="server" ID="filterRepeater">
    <ItemTemplate>
      <%# Eval("DisplayName") %>:
      <asp:DynamicFilter ID="DynamicFilter" runat="server" />
      <br />
    </ItemTemplate>
  </asp:QueryableFilterRepeater>
  <asp:ValidationSummary runat="server"/>
  <asp:GridView ID="gridView" runat="server" DataSourceID="dataSource"
    DataKeyNames="ProductID" AutoGenerateEditButton="true"
    AllowPaging="true" AutoGenerateColumns="true"/>
  <asp:EntityDataSource ID="dataSource" runat="server"
    ConnectionString="name=NorthwindEntities" EnableUpdate="true"
    DefaultContainerName="NorthwindEntities" EntitySetName="Products" />
  <asp:QueryExtender runat="server" TargetControlID="dataSource">
    <asp:DynamicFilterExpression ControlID="filterRepeater" />
  </asp:QueryExtender>
</asp:Content>


QueryableFilterRepeater is another control provided by Dynamic Data. It is somewhat similar to the EntityTemplate control used in entity templates. The QueryableFilterRepeater control instantiates its ItemTemplate for every column in the MetaTable that has an appropriate filter template. This example uses a data binding expression, <%# Eval("DisplayName") %>, to render the display name of the column and a DynamicFilter to render the appropriate filter template. Instead of creating dynamic filter expressions for the individual dynamic filter controls, the QueryExtender is configured with a single expression pointing to the filter repeater itself. Figure 1.17 shows the resulting page.

Image

Figure 1.17. QueryExtender and QueryableFilterRepeater Sample.

You might be disappointed to see that only three filters were generated for the entire Products table that contains almost a dozen of different columns. That is because Dynamic Data project template offers only three filter templates out of the box—Boolean, Enumeration, and ForeignKey. However, as you will see in Chapter 5, it is straightforward to create your own filter templates for other data types.

Page Templates and URL Routing

By now, you have been introduced to the major building blocks Dynamic Data offers for creating CRUD pages. Field and entity templates are used to build pages for inserting, editing, and displaying a single record. Filter templates and the field generator are used to build list pages that allow search and display of multiple records. If you look back at the code samples, you might notice that the only thing tying them to a particular table is the configuration of the EntityDataSource control in page markup. By simply changing the page to initialize the EntitySetName property of the EntityDataSource programmatically, perhaps based on a URL parameter, you could reuse a single page for any number of different tables. This is precisely what the Dynamic Data page templates do.

Out of the box, Dynamic Data project template offers five page templates, which are shown in Figure 1.18.

Image

Figure 1.18. Page templates in a Dynamic Data project.

Page templates rely on URL routing to determine the table they need to display and require additional setup. Listing 1.19 shows how this is done in the Global.asax file of the web application.

Listing 1.19. Registering Dynamic Data URL Route in Global.asax.cs


using System;
using System.Web.DynamicData;
using System.Web.Routing;
using DataModel;

namespace WebApplication
{
  public class Global : System.Web.HttpApplication
  {
    public static readonly MetaModel DefaultModel = new MetaModel();

    void Application_Start(object sender, EventArgs e)
    {
      var modelConfiguration = new ContextConfiguration();
      modelConfiguration.ScaffoldAllTables = true;
      DefaultModel.RegisterContext(typeof(NorthwindEntities), modelConfiguration);
      RouteTable.Routes.Add(new DynamicDataRoute("{table}/{action}.aspx"));
    }
  }
}


In particular, you need to create a DynamicDataRoute and register it in the global RouteTable. The route object defines a special URL format where the traditional QueryString parameters become a part of the virtual path. In this example, the route defines a {table} parameter, so instead of a URL like

http://server/DynamicData/PageTemplates/Edit.aspx?table=Products

you could simply type

http://server/Products/Edit.aspx.

Another parameter in this route is {action}. Action usually matches the name of one of the available page templates, such as the built-in List, Details, Edit, and Insert.

In Listing 1.19, you might have also noticed that in this code the NorthwindEntities data model is registered with a ContextConfiguration object. The ContextConfiguration allows you to provide additional information to Dynamic Data regarding the data model, such as specify that all tables should be scaffolded by default. Scaffolding a table means making it visible to the user in the UI generated by Dynamic Data. Usually, you scaffold only those few tables you want your user to be able to view and edit directly. However, in this example, all of them are scaffolded to illustrate the use of page templates.

Figure 1.19 shows the default home page provided for web applications by the Dynamic Data project template.

Image

Figure 1.19. Default home page of a Dynamic Data Web Application.

This page, Default.aspx, uses GridView and DynamicHyperLink controls to display the list of all visible tables in the data model. Clicking a table link on the Default.aspx page opens a web page similar to that shown in Figure 1.20. Notice the URL in the address bar of the web browser:

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

Image

Figure 1.20. Web page produced by the List page template.

As discussed previously, this is a virtual URL. There is no physical Products folder in this sample project. Instead, ASP.NET used the DynamicDataRoute configured in the Global.asax to pass the HTTP request to the List.aspx in the DynamicData/PageTemplates folder.

The List page template is similar to the web pages built from scratch earlier in this chapter. It also includes a EntityDataSource and a GridView controls but configures them slightly differently, as shown here:

protected MetaTable table;

protected void Page_Init(object sender, EventArgs e)
{
  this.table = DynamicDataRouteHandler.GetRequestMetaTable(this.Context);
  this.gridView.SetMetaTable(this.table,
    this.table.GetColumnValuesFromRoute(this.Context));
  this.dataSource.EntityTypeFilter = this.table.EntityType.Name;
}

Notice the call to the GetRequestMetaTable method of the DynamicDataRouteHandler class. This method extracts the table name from the URL and finds the matching table in the default MetaModel. Having obtained the required MetaTable, this code associates it with the GridView control and configures the EntityDataSource.


Note

Setting the EntityTypeFilter property as opposed to the EntitySetName property of the EntityDataSource control allows this code to work correctly not only with the simple, table-per-type mapping that has been used thus far in the Northwind sample. It also supports the more advanced table mappings offered by the Entity Framework, which are discussed in Chapter 2, “Entity Framework.”


The List page template also uses the DynamicHyperLink control in a databound GridView:

<asp:TemplateField>
  <ItemTemplate>
    <asp:DynamicHyperLink runat="server" Action="Edit" Text="Edit" />&nbsp;
    <asp:LinkButton runat="server" CommandName="Delete" Text="Delete" />&nbsp;
    <asp:DynamicHyperLink runat="server" Text="Details" />
  </ItemTemplate>
</asp:TemplateField>

When bound to a single entity object, the DynamicHyperLink generates a URL with additional parameters. Action property of the DynamicHyperLink class specifies the name of the page template, Edit in this example. When bound to a Product with the primary key column (ProductID) equal to one, this control generates a URL that looks like http://localhost/Products/Edit.aspx?ProductID=1. Figure 1.21 shows the web page displayed by this link.

Image

Figure 1.21. Web page produced by the Edit page template.

Another page template, Edit.aspx, generates this page. Similar to the List.aspx page template, it extracts table name from the request URL and configures its EntityDataSource control. Although there are some additional controls that have not been discussed, this template is very similar to the page created from scratch in the Entity Templates section of this chapter.

The Insert and Details page templates are similar to the Edit template and provide user interface for creating a new record or displaying an existing record in read-only mode respectively.

Image

Figure 1.22. Web pages produced by the Insert and Details page templates.

Summary

Dynamic Data is an extension of the ASP.NET WebForms framework that consists of three major building blocks: metadata, templates, and controls.

The metadata API relies on a LINQ-based data access framework, such as Entity Framework or LINQ to SQL, and provides a common programming interface focused on the business and presentation logic. When the information offered by the underlying data access framework is insufficient, the metadata API relies on Data Annotations, the .NET attributes defined in the System.ComponentModel.DataAnnotations namespace, to provide information about display names of columns and tables, order, and visibility of the individual columns and more.

The Dynamic Data templates implement individual user interface fragments of the web application. Field templates are ASP.NET user controls that encapsulate user interface necessary for a single field value. Entity templates are ASP.NET user controls that rely on multiple field templates to implement user interface for an entire database record in a form layout. Filter templates are ASP.NET user controls that implement logic required to enter search criteria and generate a LINQ query based on a single column. Page templates are ASP.NET pages that use entity templates and implement the supporting logic for navigation and CRUD operations. Dynamic Data projects provide a number of different templates out of the box and enable developers to create new templates to fit their specific project needs.

The Dynamic Data controls use the metadata information to automatically load and display appropriate templates. DynamicControl can be used with the FormView and ListView controls to load an appropriate field template. DynamicField serves a similar purpose; however, it is designed for use with the GridView and DetailsView controls. The DynamicEntity control automatically loads an appropriate entity template, and the DynamicFilter control automatically loads an appropriate filter template. Although these controls are used extensively in the Dynamic Data templates, developers can use them in regular pages and controls of WebForms applications as well.

This chapter has barely scratched the surface of the functionality offered by the ASP.NET Dynamic Data. The remaining chapters of Part I offer a closer look at each of these areas.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.252.204