Dan Maharry

Writing about web development since 1997

ASP.NET 4.0 Part 15, Data Enhancements

Welcome to part 15 of my tour through ASP.NET 4.0. In this episode, we're going to look at how webforms developers can make more use of both Dynamic Data 4.0 and Entity Framework 4.0 thanks to several new features baked into ASP.NET 4.0.

For those of you unfamiliar with these two products, Entity Framework is the 'enterprise-level' data modeller (O/RM) technology, aimed at larger applications where LINQ2SQL was aimed at small ones. Dynamic Data meanwhile is a rapid development tool used to create a basic scaffolding for a website based on the schema of the database that supports. Almost a CSS-like construct based on the type of data in a field rather than the type,class or id of an HTML element.

It's odd how both Dynamic Data (DynData) and the Entity Framework (EF)  were both released in .NET 3.5 SP1 almost as a tester to garner public reaction to Microsoft's efforts thus far with both projects. And it's fair to say that mistakes / omissions were made in those v1 releases which are now being corrected. EF was vilified in comparison to other, more mature OR/M frameworks for many reasons including

  • Short list of shortcomings that have been fixed in EF 4
  • Unit testing shortfalls. EF objects couldn't be generated as plain old CLR objects (POCOs) - they had to be classes derived from EntityObject which meant they would hit the db when tests were run against them.
  • EF didn't support many to many relationships

DynData, which sat on top of EF, suffered from both EF's shortcomings and some of its own including.

  • DynData 3.5 required you to start a new project and coudn't easily be integrated into a webforms or MVC application
  • If you used EF as the data model, DynData couldn't tell which field in a database table was the primary key and would model it as an editable field. You had to go into the model and annotate it separately.
  • Scaffolding templates for only basic field types and none for entities (types defined across several table joins)

Needless to say, both DynData 4.0 and EF 4.0 address these issues and many more. It's now fair to say that both technologies are well beyond where their first versions perceptibly fell short.

EnableDynamicData

One of the goals of ASP.NET 4.0 was to make DynData's scaffolding available to both Webforms and MVC. As noted earlier, DynData v1 was a project of its own that couldn't be easily retrofitted into an existing website, be it using Webforms or MVC. To remedy this, ASP.NET 4.0 now includes a new method called EnableDynamicData that you can use on any page in a website or web application.

Consider the scenario where you've a FormView or GridView allowing you to update, add to or delete records from a database. Typically, the controls auto-generate columns containing textboxes and maybe a checkbox. Users can add in invalid text, dates, values outside desired ranges etc. So you disable auto-generation and template the columns yourself with a calendar control for dates, say, and validation controls. And then you do the same thing for various other data-bound controls based on the same tables of data. Rather than setting all columns and rows yourself on each control, the EnableDynamicData method tells ASP.NET to generate those customized input and validation controls itself using the DynData scaffolding generated against the database and annotations you may give it as a (central) template.

Let's take an example.

  • Open VS2010 and create a new, empty web site.
  • If you’re using SQL Express, add an App_Data folder and add in the AdventureWorksLT database. You’ll find it for download on codeplex.
  • Add add an Entity Data Model to your site. Generate it from the tables in the newly added database.
  • Add a new page to your site and place on it an EntityDataSource and a GridView. The EntityDataSource should retrieve the EntitySet for the Products table. The GridView should use the EntityDataSource and both should have Update and Deletes enabled.

Your page code should look like this.

<%@ Page Language="C#" AutoEventWireup="true" 
         CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Demo</title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:GridView runat="server" DataSourceID="entityDs" ID="gvwProducts">
      <Columns>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
      </Columns>
    </asp:GridView>
    <asp:EntityDataSource runat="server" ID="entityDs" 
         ConnectionString="name=AdventureWorksLT2008_DataEntities" 
         DefaultContainerName="AdventureWorksLT2008_DataEntities" 
         EnableDelete="True" EnableFlattening="False" 
         EnableUpdate="True" EntitySetName="Products"
         EntityTypeFilter="Product"> </asp:EntityDataSource>   
    </div>
  </form>
</body>
</html>

If you run this page and try to update any of the currency-based fields for an item now listed in the grid (eg. StandardCost or ListPrice) with a non-currency value such as the string ‘abc’, you’ll get the following Yellow Screen Of Death message.

Error while setting property 'StandardCost': 'Cannot convert the value of parameter 'StandardCost' to the type 'System.Decimal'.'.

Of course, if we were to manually generate the columns in the GridView, we could add in validation to ensure that only valid currency values are sent to the database. But let’s not and enable the dynamic data framework on the GridView and see what happens. It’s a single line of code added to your page’s codebehind.

public partial class _Default : System.Web.UI.Page
{
  protected void Page_Init(object sender, EventArgs e)
  {
    gvwProducts.EnableDynamicData(typeof(AdventureWorksLT_DataModel.Product));
  }
}

If you run the page again and try to make the same page, you’ll see that some validation is performed pre-save and so clicking Update on the grid doesn’t produce the previously seen yellow screen of death.

Simple Validation

Admittedly, it’s not earth-shatteringly useful as the invalid value is simply highlighted by an asterisk to its side with the actual error message that StandardCost is a required value hidden in a tooltip, but by adding a ValidationSummary control to the page, things become much clearer. And we’re leveraging the Dynamic Data framework to generate validation for us, which we can always amp up by adding instructions centrally to the entity model such as an appropriate error message. For example,

namespace AdventureWorksLT_DataModel
{
  [MetadataType(typeof(ProductMetadata))]
  public partial class Product
  {  }

  public class ProductMetadata
  {
    [RegularExpression(@"^[0-9]{1,}\.[0-9]{2}$", 
       ErrorMessage="Value should be greater than 0.00")]
    public decimal StandardCost { get; set; }
  }
}

Run the page again and you’ll see that a RegularExpressionValidator is now added to the RequiredFieldValidator already there.

There are loads of new features in Dynamic Data 4.0 that we can now leverage in WebForms and MVC including. 

  • Primary keys are now identified as primary keys and are not shown as an editable field
  • New field templates for URLs and email addresses.
  • Many-to-many relationships are now handled as a field template

Entity templates are another new feature in Dynamic Data 4.0. Whereas field templates deal with single field types, entity templates deal with objects as a whole, be they contained in a single table or spanned across many. Let's say we've a website in which the information for an auction item is restricted based on whether you're an admin, the seller or a buyer. An entity template means you can control what each role sees at the object level rather than reiterating it across several pages where that object is bound to controls.

Entity and field templates are available to both MVC and Webforms.

QueryExtender

If there's one problem with LINQ-based DataSource controls (for example, LinqDataSource and EntityDataSource) in .NET 3.5 it's that filtering on the data isn't supported declaratively by the data source. You have to take control of the query being sent to the database, add the filtering yourself and then passing the query back to the data source, usually by handling the OnQueryCreated event.

protected void dsProducts_QueryCreated(object sender, QueryCreatedEventArgs e)
{
  if (!String.IsNullOrWhiteSpace(txtFilterName.Text))
  {
    e.Query = from p in e.Query.Cast<AdventureWorksLT_DataModel.Product>()
              where p.Name.StartsWith(txtFilterName.Text.Trim())
              select p;
  }
}

In .NET 4.0 however, there is now a solution - the new QueryExtender control. Let's take an example and reuse the page we've got going. It already pulls down the contents of the Products table from the AdventureWorks table. Let's add a simple textbox which let's you set the start of the product's name to search for.

<body>
  <form id="form1" runat="server">
  <div>
    Name starts with:
    <asp:TextBox ID="txtFilterName" runat="server">Sport</asp:TextBox>
    <asp:Button ID="btnGo" runat="server" Text="Go" />
    <br />
    <asp:GridView runat="server" DataSourceID="entityDs" ID="gvwProducts">
...

To include the filtering in the page, we add the QueryExtender to the page as follows.

<asp:QueryExtender runat="server" ID="qeFilter" TargetControlID="entityDs">
  <asp:SearchExpression DataFields="Name" SearchType="StartsWith">
    <asp:ControlParameter ControlID="txtFilterName" PropertyName="Text" />
  </asp:SearchExpression>
</asp:QueryExtender>

Et voila. As the page loads, the QueryExtender informs the EntityDataSource and filters the data it presents us based on the contents of the text box.

Summary

There's a heck of a lot of new and interesting stuff going on in and around the world of data in .NET. Besides the advancements to the Dynamic Data and Entity Frameworks, there's also a new version of ADO Data Services (now known as WCF data services), the introduction of data storage in the cloud with SQL Azure Services and client-side data binding with AJAX Data Templates.

In our look at one small corner of it all, we saw how the EnableDynamicData method allows us to leverage the Dynamic Data Framework to act as a CSS-like form presentation and validation rules template for all our templated, data bound controls - a level of integration between Dynamic Data and Webforms/MVC previously unavailable.

We also saw how we can leverage the new QueryExtender control against LinqDataSource and EntityDataSource controls to act as dynamic filters for the data they retrieve.

Comments (4) -

  • Duncan Smart

    4/8/2010 10:22:53 AM |

    Thanks Dan, excellent overview. Although, I wish I knew EF and DD were effectively broken in .NET 3.5 before I started messing about with them last week!

  • David

    4/9/2010 10:00:02 AM |

    Hi there,

    I *LOVE* that meta validation stuff! I was sure that's doable only when doing POCO. Any idea if there's a performance hit when using it?

    Thanks!

  • David

    4/9/2010 11:40:16 AM |

    Ok, after reading about it here I did some research and if I got that right you need to create the meta type and then you always need to enable dynamic data on the specific control that gonna use it and specify a specific type that's gonna be processed with the dynamic data stuff that we attached to it.

    Sounds very impractical when you want to implement this approach on a full EDMX with dozens of classes and to use it throughout your solution.

    Am I missing anything?

  • DanM

    4/9/2010 11:40:54 PM |

    @David, Regrettably no. You're not missing anything. I think MS is taking the same tack as with opt-in viewstate, assuming that only a few databound items will need to be enabled. If that's not the case, you might want to look at starting the project as a Dynamic Data web app project rather than the web app. That might be more feasible.
    Dan

Comments are closed