Understanding ASP.NET Dynamic Data: Entity Templates
The only option to create a custom form layout in version 3.5 of the ASP.NET Dynamic Data was to create an entire custom page, as shown in this video and explained in this article. Although powerful, this approach requires you to duplicate an entire page, with all of its supporting code. Version 4.0 of ASP.NET Dynamic Data allows you to define entity templates which contain only the layout of a form. This article explains how Dynamic Data scaffolds entity templates, walks you through the process of creating a simple entity template and shows how default entity templates supplied by Dynamic Data can be modified to reduce the amount of code duplication.
Overview of Entity Templates
In version 4.0 of ASP.NET Dynamic Data, entity templates are responsible for displaying data from a single entity (i.e. data row) in a FormView. The screenshots below show the Insert, Edit and Details pages with parts generated by the the entity templates outlined in red.
Here is how the default Details.aspx page template uses the read-only Details entity template.
<asp:FormView runat="server" ID="FormView1" DataSourceID="DetailsDataSource"> <ItemTemplate> <table> <asp:DynamicEntity runat="server" /> <tr> <td> <asp:DynamicHyperLink runat="server" Action="Edit" Text="Edit" /> <asp:LinkButton runat="server" CommandName="Delete" Text="Delete"/> </td> </tr> </table> </ItemTemplate> </asp:FormView>
Notice the DynamicEntity control in the middle of the FormView above. During the load stage of the ASP.NET page lifecycle, DynamicEntity control finds the appropriate user control in the EntityTemplates folder of the Dynamic Data application and dynamically creates it to provide entity-specific portion of the page.
Here is how the default entity template (EntityTemplates\Default.ascx) user control is implemented.
<%@ Control Language="C#" CodeBehind="Default.ascx.cs" Inherits="DynamicDataDemo.DefaultEntityTemplate" %> <asp:EntityTemplate runat="server" ID="EntityTemplate1"> <ItemTemplate> <tr> <td> <asp:Label runat="server" OnInit="Label_Init" /> </td> <td> <asp:DynamicControl runat="server" OnInit="DynamicControl_Init" /> </td> </tr> </ItemTemplate> </asp:EntityTemplate>
This user control inherits from the EntityTemplateUserControl class provided by Dynamic Data. This base class provides access to several properties worth mentioning, such as Table, which gives you the MetaTable describing the current entity, and Mode, which determines whether the entity should be displayed in edit, insert or read-only mode. The EntityTemplate control, provided by Dynamic Data, is essentially a repeater that our entity template user control uses to generate HTML for each property of the current entity. DynamicControl, also provided by Dynamic Data, dynamically loads a field template appropriate for the current property, based on its type and attributes. All that is needed is a little bit of code (shown below) to wire up the controls.
using System; using System.Collections.Generic; using System.Web.DynamicData; using System.Web.UI; using System.Web.UI.WebControls; namespace DynamicDataDemo { public partial class DefaultEntityTemplate : EntityTemplateUserControl { private MetaColumn currentColumn; protected override void OnLoad(EventArgs e) { IEnumerable<MetaColumn> columns = this.Table.GetScaffoldColumns( this.Mode, this.ContainerType); foreach (MetaColumn column in columns) { this.currentColumn = column; Control item = new _NamingContainer(); this.entityTemplate.ItemTemplate.InstantiateIn(item); this.entityTemplate.Controls.Add(item); } } protected void DynamicControl_Init(object sender, EventArgs e) { DynamicControl dynamicControl = (DynamicControl)sender; dynamicControl.DataField = this.currentColumn.Name; } protected void Label_Init(object sender, EventArgs e) { Label label = (Label)sender; label.Text = this.currentColumn.DisplayName; } public class _NamingContainer : Control, INamingContainer { } } }
Most of the magic happens in the OnLoad method of the user control. To determine which controls to display, it calls the GetScaffoldColumns method of the MetaTable class. This method returns a list of MetaColumn objects, each describing a single property of our entity, including property’s Name and DisplayName. Having obtained the list of MetaColumns, the OnLoad method instantiates the EntityTemplate for each column to generate the controls. Essentially, this code implements a form of the templated user control, a well established ASP.NET pattern.
The rest of the magic is done by the DynamicControl instances generated for each column. As you can see in the DynamicControl_Init method, we initialize its DataField property with the name of the current MetaColumn. During the load stage, DynamicControl will automatically find an appropriate field template and include it in the page as described here.
Entity Template Scaffolding
Out of the box, Dynamic Data provides three entity templates called Default, Default_Insert and Default_Edit. The Default entity template is used by the Details page and the Default_Insert and Default_Edit entity templates are used by the Insert and Edit pages respectively.
The entity template user controls follow a specific naming convention:
{Name}.ascx
{Name}_{Mode}.ascx
- Name is the name of a MetaTable or the UIHint that can be specified explicitly on the DynamicEntity control. Typically, this will be the name of a MetaTable, which will match the name of the property generated in your context class you would normally use to access the data, such as Products or Customers. However, if you use inheritance in your data model, table name will match the class name for derived entity types and will typically have singular form, such as Customer.
- Mode can be either Edit or Insert. An entity template that doesn’t include mode in its name is assumed to be a ReadOnly template.
During the load stage of control lifecycle, the DynamicEntity control uses EntityTemplateFactory of the default MetaModel to find and create a matching entity template user control. The factory will first try the most specific name, such as Products_Insert.ascx. If a file with this name does not exist, it will try changing the name and the mode portions of the file name until it locates the first suitable match. The table below shows the order used by the EntityTemplateFactory to find entity templates.
| Insert Mode | Edit Mode | Read-only Mode |
| {UIHint}_Insert.ascx {UIHint}_Edit.ascx {TableName}_Insert.ascx {TableName}_Edit.ascx Default_Insert.ascx Default_Edit.ascx {UIHint}.ascx {TableName}.ascx Default.ascx |
{UIHint}_Edit.ascx {TableName}_Edit.ascx Default_Edit.ascx {UIHint}.ascx {TableName}.ascx Default.ascx |
{UIHint}.ascx {TableName}.ascx Default.ascx |
Creating a Custom Entity Template
If you are not satisfied with the single-column layout that default entity templates produce, you can create a custom entity template by creating a special user control with the name that matches your the name of your entity and the mode of your page and placing it in the EntityTemplates folder of your Dynamic Data web project. The screenshot on the right shows output produced by the Details.aspx page using a custom Products.ascx entity template. Again, output of the entity template is outlined in red.
Here is the implementation of the EntityTemplates\Products.ascx (without the formatting attributes, which were removed for clarity).
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Products.ascx.cs" Inherits="DynamicDataDemo.DynamicData.EntityTemplates.ProductsEntityTemplate" %> <tr> <td> <table> <tr> <td>Name</td> <td> <asp:DynamicControl runat="server" DataField="ProductName" OnInit="DynamicControl_Init"/> </td> </tr> <tr> <td>Category</td> <td> <asp:DynamicControl runat="server" DataField="Category" OnInit="DynamicControl_Init"/> </td> </tr> </table> </td> <td> <table> <tr> <td>Supplier</td> <td> <asp:DynamicControl runat="server" DataField="Supplier" OnInit="DynamicControl_Init"/> </td> </tr> <tr> <td>Units in stock</td> <td> <asp:DynamicControl runat="server" DataField="UnitsInStock" OnInit="DynamicControl_Init"/> </td> </tr> </table> </td> </tr>
Notice that we use DynamicControl instances to dynamically generate appropriate field templates for the properties we want to see – ProductName, Category, Supplier and UnitsInStock. For ProductName, DynamicControl will choose the Text.ascx field template, which will produce plain text output, but for the foreign key columns, Category and Supplier, it will choose the ForeignKey.ascx field template, which will produce hyperlinks to the dynamic Details.aspx pages for the Category and Supplier entities respectively.
Our custom entity template defines the required layout for the Product entity, without re-implementing any of the field presentation logic. This power becomes even more apparent in Insert and Edit pages (shown below). The editable versions of the field templates make extensive use of not only web controls, but also client- and server-side validators. Although this logic would be easy to implement by copy-and-paste initially, consistently maintaining it across different custom pages and controls quickly becomes a serious maintenance challenge. Thanks to the DynamicControl we can maintain this logic in a single place – the field templates and reuse it in the custom entity templates without incurring the penalty of code duplication.

In order to make the field templates work correctly in Edit and Insert mode, we need to dynamically initialize the Mode property for each DynamicControl in the template. This is done in the DynamicControl_Init method defined in the code-behind. During the initialization stage of control lifecycle, DynamicControl passes this value to the FieldTemplateFactory to locate and dynamically create the editable versions of the field templates.
using System; using System.Web.DynamicData; namespace DynamicDataDemo.DynamicData.EntityTemplates { public partial class ProductsEntityTemplate : EntityTemplateUserControl { protected void DynamicControl_Init(object sender, EventArgs e) { DynamicControl dynamicControl = (DynamicControl)sender; dynamicControl.Mode = this.Mode; } } }
Improving Default Entity Templates
Unfortunately, our Products.ascx cannot be used by the Edit.aspx page template immediately. In Edit mode, without an entity template called Products_Edit.ascx, the EntityTemplateFactory will try Default_Edit.ascx before it tries the Products.ascx. In order to make a custom entity template for Edit and Insert mode, we would need to save a copy of Products.ascx as Products_Edit.ascx. This last template would be reused by both Edit.aspx and Insert.aspx because the EntityTemplateFactory assumes that edit-mode entity templates can be used in insert mode as well.
Having two identical copies of the same code is not ideal and will quickly get out of hand as the number of custom entity templates grows. Your first attempt to solve the code duplication problem might be to extract this code into a user control, such as ProductsTemplate.ascx, and reuse it in both Products.ascx and Products_Edit.ascx. This approach works, but you end up with even more files in your project, three user controls for each entity that needs a custom template, all in order to work around the logic the EntityTemplateFactory uses to resolve entity templates. There has to be a better way!
Taking a closer look at the Default.aspx, Default_Edit.ascx and Default_Insert.ascx entity templates provided by the Dynamic Data, you will notice that these user controls are very similar. We can easily combine their logic into the Default.ascx and delete the Default_Edit.ascx and Default_Insert.ascx. This will not only eliminate the code duplication in the default entity templates; it will also eliminate the need to duplicate code or create multiple files for custom entity templates as well.
Here is what the new Default.ascx looks like without formatting attributes.
<%@ Control Language="C#" CodeBehind="Default.ascx.cs" Inherits="DynamicDataDemo.DefaultEntityTemplate" %> <asp:EntityTemplate runat="server" ID="entityTemplate"> <ItemTemplate> <tr> <td> <asp:Label runat="server" OnInit="Label_Init" OnPreRender="Label_PreRender" /> </td> <td> <asp:DynamicControl ID="dynamicControl" runat="server" OnInit="DynamicControl_Init" /> </td> </tr> </ItemTemplate> </asp:EntityTemplate>
Note the addition of the OnPreRender event handler for the Label control. The Label_PreRender method associates the label with the dynamically generated DataControl. Another important difference is how the Mode property of the DynamicControl is initialized. In the templates supplied by Dynamic Data, this is done in the control markup. In our new template this is done dynamically, in the DynamicControl_Init method, which initializes it based on the Mode of the entity template itself.
Here is the new code-behind file, Default.ascx.cs.
using System; using System.Collections.Generic; using System.Web.DynamicData; using System.Web.UI; using System.Web.UI.WebControls; namespace DynamicDataDemo { public partial class DefaultEntityTemplate : EntityTemplateUserControl { private MetaColumn currentColumn; protected override void OnLoad(EventArgs e) { IEnumerable<MetaColumn> columns = this.Table.GetScaffoldColumns( this.Mode, this.ContainerType); foreach (MetaColumn column in columns) { this.currentColumn = column; Control item = new _NamingContainer(); this.entityTemplate.ItemTemplate.InstantiateIn(item); this.entityTemplate.Controls.Add(item); } } protected void DynamicControl_Init(object sender, EventArgs e) { DynamicControl dynamicControl = (DynamicControl)sender; dynamicControl.DataField = this.currentColumn.Name; dynamicControl.Mode = this.Mode; } protected void Label_Init(object sender, EventArgs e) { Label label = (Label)sender; label.Text = this.currentColumn.DisplayName; } protected void Label_PreRender(object sender, EventArgs e) { Label label = (Label)sender; DynamicControl dynamicControl = (DynamicControl)label.FindControl("dynamicControl"); FieldTemplateUserControl fieldTemplate = dynamicControl.FieldTemplate as FieldTemplateUserControl; if (fieldTemplate != null && fieldTemplate.DataControl != null) { label.AssociatedControlID = fieldTemplate.DataControl.GetUniqueIDRelativeTo(label); } } public class _NamingContainer : Control, INamingContainer { } } }
Closing Thoughts
Entity templates are a very powerful addition to the ASP.NET Dynamic Data. A single default entity template user control can define the form layout for all entity types in your model in three different modes – ReadOnly (Details.aspx), Insert (Insert.aspx) and Edit (Edit.aspx). A custom entity template can be quickly created to replace the standard single-column form layout with a required custom layout, which again, can be reused in all three different modes. This power truly shines in Insert and Edit pages, where entity templates give you the ability to modify the form layout logic completely independently from the data entry and validation logic encapsulated by the field templates. While the default entity templates supplied by the Dynamic Data project are mode-specific and encourage code duplication, you can easily combine them into a single entity template and eliminate the need to create mode-specific custom entity templates as well.



[…] Understanding ASP.NET Dynamic Data: Entity Templates (Oleg Sych) […]