Understanding ASP.NET Dynamic Data: Filter Templates
ASP.NET Dynamic Data lets you create extensible model-driven Web applications by inferring the appearance and behavior of entities from the model at run time. It relies on “scaffolding” to automatically generate different types of Web pages for each entity in the model from a set of reusable ASP.NET user controls and pages. Each of these “templates” encapsulates a particular type of functionality, such as a filter control or a data entry form, and can be reused automatically for all entities in the model. ASP.NET Dynamic Data supports template scaffolding for pages, entities, fields and filters. A good step-by-step introduction can be found here. In this article we will be focusing on scaffolding of the filter templates.
Queryable Data Sources
Dynamic Data relies on LINQ and deferred query execution for filtering. Here is how we might go about filtering the list of orders in the Northwind sample database to return only those placed after a given date using static code and the ADO.NET Entity Framework.
NorthwindEntities context = new NorthwindEntities(); IQueryable<Order> query = context.Orders; query = query.Where(order => order.OrderDate > new DateTime(1996, 7, 15)); Order[] orders = query.ToArray();
In line two of this example, we create a deferred query object that, when executed, would return all orders from the database. In line three, we replace the original query with a new one, which has a where clause to filter the orders based on the order date. Up until now, the query is stored in a form of a LINQ Expression tree in memory of the executing program. Only in line four the ADO.NET Entity Framework transforms the query into a SQL SELECT statement and sends it to the database for execution. Once the response comes back from the database, the Entity Framework uses the returned data to create an array of Order objects.
Deferred query execution allows us to add practically any number of filters to the original query before it is sent to the database. Dynamic Data takes advantage of this powerful capability using the extensibility mechanisms built into the LINQ-based data source classes of ASP.NET 4.0 – LinqDataSource and EntityDataSource.
Both of these classes implement the IQueryableDataSource interface which exposes the QueryCreated event. A queryable data source object raises this event after it has created the original LINQ query, before sending it to the database. QueryCreated event handlers receive a QueryCreatedEventArgs object which allows you to replace the original the original Query, similar to how the code in line three of our example does.
To allow creating filters declaratively, using page markup, ASP.NET also provides a control called QueryExtender. QueryExtender is typically placed on a page and linked to a data source using TargetControlID property. Under the hood, the control handles the data source’s QueryCreated event and uses its own collection of DataSourceExpression objects to modify the query.
<asp:EntityDataSource ID=”dataSource” runat=”server” /> <asp:QueryExtender TargetControlID=”dataSource” ID=”queryExtender” runat=”server”> <asp:DynamicFilterExpression ControlID=”filterRepeater” /> </asp:QueryExtender>
DataSourceExpression defines an abstract method called GetQueryable. QueryExtender passes the original query to GetQueryable of first DataSourceExpression, takes the query it returns and passes it to the second DataSourceExpression, and so on, giving each expression the chance to modify the original query by adding a filter, changing its sort order, etc.
Dynamic Filters
DynamicFilterExpression inherits from the DataSourceExpression and overrides its GetQueryable method to take advantage of rich filter templating capabilities provided by the ASP.NET Dynamic Data. Using ControlID property, DynamicFilterExpression is typically associated with a QueryableFilterRepeater, another Dynamic Data control.
<asp:QueryableFilterRepeater runat=”server” ID=”FilterRepeater”> <ItemTemplate> <asp:Label runat=”server” Text=’<%# Eval(”DisplayName”) %>‘ OnPreRender=”Label_PreRender” /> <asp:DynamicFilter runat=”server” ID=”DynamicFilter” OnFilterChanged=”DynamicFilter_FilterChanged” /> <br /> </ItemTemplate> </asp:QueryableFilterRepeater>
QueryableFilterRepeater is a templated control, similar to the standard Repeater. It generates a DynamicFilter for each column in the queryable data source that can be filtered by Dynamic Data.
DynamicFilter control uses the filter scaffolding functionality of Dynamic Data to locate and load an appropriate user control from the ~/DynamicData/Filters folder of the web application. These user controls inherit from QueryableFilterUserControl, a base class provided by Dynamic Data and designed for use with DynamicFilter.
Notice that all of the classes in the diagram above – DynamicFilterExpression, QueryableFilterRepeater, DynamicFilter and QueryableFilterUserControl – define a GetQueryable method that takes an IQueryable as a parameter and returns an IQueryable. At run time, the responsibility to modify the underlying LINQ query (an IQueryable instance) is passed from a QueryExtender all the way down the chain to a QueryableFilterUserControl, which was implemented to support filtering for a particular type of column, such as a Boolean or a foreign key.
Filter Controls
Out of the box, ASP.NET Dynamic Data provides filter controls for Boolean, Enumeration and Foreign Key columns. This selection is rather limited, especially when you compare it to the wide range of edit controls available for the various column types. Luckily, creating a new filter template is relatively easy.
In the remainder of this article we will create a template filter for columns of DateTime type. The filter control will allow users to filter date values based on a date value they enter and the operator they select, such as equals, before and after. The completed control will look similar to the picture below.
- Add a new user control, DateTime.ascx, to the ~/DynamicData/Filters folder of your Dynamic Data web application. Name of the ASCX file is important, it will be used to provide a filter hint to Dynamic Data.
<%@ Control Language=”C#” CodeBehind=”DateTime.ascx.cs” Inherits=”DynamicDataDemo.DateFilter” %> <asp:DropDownList ID=”dropDownList” runat=”server” CssClass=”DDFilter”> <asp:ListItem Text=”Equals” Value=”==” /> <asp:ListItem Text=”Before” Value=”<” /> <asp:ListItem Text=”After” Value=”>” /> </asp:DropDownList> <asp:TextBox ID=”textBox” runat=”server” CssClass=”DDTextBox” Columns=”12″ /> <asp:CustomValidator ID=”validator” runat=”server” ControlToValidate=”textBox” OnServerValidate=”Validate” Text=”*” CssClass=”DDValidator”/>
In this ASCX, we have a drop-down list that displays a list of date operators the users can select from, and a text box, where they can enter a date value. The custom validator, used for the sake of simplicity, ensures that the value they enter in the text box is a valid date.
- In the code-behind file, change the base class from UserControl to QueryableFilterUserControl. Here is what it will look like at a glance.

- GetQueryable is the main method in our filter control. Defined as abstract in the base class, this method is responsible for adding a filter to the query it receives.
public override IQueryable GetQueryable(IQueryable source) { if (string.IsNullOrEmpty(this.textBox.Text) || !this.validator.IsValid) { return source; } DateTime date = DateTime.Parse(this.textBox.Text); ConstantExpression value = Expression.Constant(date); ParameterExpression parameter = Expression.Parameter(source.ElementType); MemberExpression property = Expression.Property(parameter, this.Column.Name); if (Nullable.GetUnderlyingType(property.Type) != null) { property = Expression.Property(property, “Value”); } BinaryExpression comparison; switch (this.dropDownList.SelectedValue) { case “==”: comparison = Expression.Equal(property, value); break; case “>”: comparison = Expression.GreaterThan(property, value); break; case “<”: comparison = Expression.LessThan(property, value); break; default: Debug.Fail(“Unexpected operator”); return source; } LambdaExpression lambda = Expression.Lambda(comparison, parameter); MethodCallExpression where = Expression.Call( typeof(Queryable), “Where”, new Type[] { source.ElementType }, new Expression[] { source.Expression, Expression.Quote(lambda) }); return source.Provider.CreateQuery(where); }
Unfortunately, because this control can be used for any DateTime column of any entity in our model, we cannot use strongly-typed code to implement this method. Instead of IQueryable<Order> we have to write code at the next level up - with the IQueryable interface and build the LINQ expression tree for our filter from the ground up. When expressed with strongly-typed code, the intent of this method could be distilled to the following pseudo-code:
IQueryable<Order> GetQueryable(IQueryable<Order> source) { if (string.IsNullOrEmpty(this.textBox.Text) || !this.validator.IsValid) { return source; } DateTime date = DateTime.Parse(this.textBox.Text); Expression<Func<Order, bool>> lambda = null; switch (this.dropDownList.SelectedValue) { case “==”: lambda = order => order.OrderDate == date; break; case “>”: lambda = order => order.OrderDate > date; break; case “<”: lambda = order => order.OrderDate < date; break; default: Debug.Fail(“Unexpected operator”); break; } return source.Where(lambda); }
As you can see in the actual implementation above, we have to explicitly perform all tasks the compiler would normally take care for us, such as declaring the parameter of the lambda expression, extracting the underlying value from a nullable property and calling the proper version of the generic Where method based on the entity type. When looking at the actual code above, remember that Where is an extension method defined by the Queryable class, and not by the IQueryable interface.
- FilterControl property returns the control that should receive focus when user selects the Label generated for it by the QueryableFilterRepeater. In our date filter, this can be either the drop-down list or the text box.
public override Control FilterControl { get { return this.textBox; } }
- The Page_Load event handler performs control initialization tasks that would be typically done declaratively, using markup in a regular filter form where information about columns being filtered is known at design time. In a Dynamic Data filter control, this information is available at run-time through the Column property of the base class, which gives us a MetaColumn object describing the column for which our filter control was generated.
protected void Page_Load(object sender, EventArgs e) { this.textBox.ToolTip = this.Column.Description; this.validator.ErrorMessage = “Invalid date specified for ” + this.Column.DisplayName; this.validator.ToolTip = this.validator.ErrorMessage; }
- And finally, the Validate method handles the server-side validation event of the CustomValidator our control uses to ensure that a valid date was entered in the text box.
protected void Validate(object sender, ServerValidateEventArgs e) { DateTime value; e.IsValid = DateTime.TryParse(e.Value, out value); }
Filter Scaffolding
Unlike for scaffolding of the data entry controls, ASP.NET Dynamic Data doesn’t use the same convention-based logic for filter controls. In order to instruct the framework to render a custom filter control for the OrderDate of our Order table, we need to mark the OrderDate property in our model with the FilterUIHintAttribute.
- Add a new class to the project that contains your Entity Framework (EDMX) or LINQ to SQL (DBML) file and generated entity classes.
using System.ComponentModel.DataAnnotations; namespace DynamicDataDemo { [MetadataType(typeof(Order.Metadata))] partial class Order { public class Metadata { [FilterUIHint(“DateTime”)] public object OrderDate; } } }
Taking advantage of the fact that Entity Framework generates entity classes as partial, we can use MetadataTypeAttribute to extend it with a custom metadata class and place an attribute on a property defined in the generated code without modifying generated code directly and risking loosing our changes the next time it is regenerated.
- Place a FilterUIHintAttribute on the property you want to have a filter control. The hint string you specify must match the name of the user control file (DateTime.ascx) without the extension.
Filter Criteria Change Notification
If you are to run your ASP.NET Dynamic Data application now, you should be able to see the new filter for the OrderDate column on the Orders/List.aspx page. However, you will also notice that entering a date in the text box does not filter the results displayed in the grid automatically. This is because our data source control is not aware that filter criteria has changed and that query needs to be executed again. All built-in filter controls are drop-down lists and solve this problem by notifying the data source as soon as the selected value changes. Our custom filter uses a text box and cannot post back to server every time user presses a key when entering a date value. Instead, we will use a Search button to let the user perform the search when they are done with the criteria.
- Modify ~/DynamicData/PageTemplates/List.aspx to add a “Search” button just below the filter criteria.
<asp:QueryableFilterRepeater runat=”server” ID=”FilterRepeater”> <ItemTemplate> <asp:Label runat=”server” Text=’<%# Eval(”DisplayName”) %>‘ OnPreRender=”Label_PreRender” /> <asp:DynamicFilter runat=”server” ID=”DynamicFilter” OnFilterChanged=”DynamicFilter_FilterChanged” /> <br /> </ItemTemplate> </asp:QueryableFilterRepeater> <br /> <asp:Button ID=”searchButton” runat=”server” Text=”Search” OnClick=”SearchButton_Click” />
- Implement SearchButton_Click method in the code-behind file.
protected void SearchButton_Click(object sender, EventArgs e) { ((IQueryableDataSource)this.GridDataSource).RaiseViewChanged(); }
In this method, we are calling the RaiseViewChanged method of the IQueryableDataSource interface to let the data source know that it needs to re-execute the query. Unfortunately, EntityDataSource implements this interface explicitly, which requires a type cast before this method can be called.
Here is how the completed Order/List.aspx page looks after the user entered an order date and clicked the Search button. Note that in the entity model, FilterUIHint attributes were specified for OrderDate, RequiredDate and ShippedDate columns of the Orders table.

Conclusion
ASP.NET Dynamic Data provides powerful filtering capabilities that can be extended to fit needs of many data-driven web applications. Although creating dynamic filter controls is significantly more complex than creating statically typed controls, a dynamic filter control developed for a particular data type can be reused for any column of that type by simply applying the FilterUIHintAttribute to the property representing that column in the model. Unfortunately, Dynamic Data does not use the same convention-based logic for scaffolding of filter controls that it uses for scaffolding of data entry controls. This limitation requires you to explicitly place the FilterUIHintAttribute on every column instead of relying on the framework to infer this information based on the column type.



Thank you very much Oleg. I have been searching “((IQueryableDataSource)this.GridDataSource).RaiseViewChanged();” for hours… You are my hero today :) I will add your rss to my outlook…I have one question for you? Where did you find this info :)