A Richer DynamicFilterRepeater: Part 1 – Extending the LinqDataSouce
Posted by admin on February 27th, 2008
Download Solution – DynamicData.zip
I’ve recently started digging into the ASP.NET Dynamic Data Framework and evaluating its usage on a new RAD web site project. The project is a short duration web site with fairly standard data entry, searching, and detail views. The Dynamic Data Framework fit the requirements well except that the dynamic searching controls did not have the required features needed for the project. I started investigating how hard it would be add the following search features:
- Searching ranges (ListPrice > 10 AND ListPrice < 500)
- Searching in a list of possible values (Class in (‘L’, ‘M’))
- Partial text searching (Color LIKE ‘B%’)
The DynamicFilterRepeater is the control provided with the Dynamic Data Framework which automatically gives the ListTemplate and ListDetailsTemplate the ability to filter on any foreign key found within the type of object listed. The DynamicFilterRepeater is a UI control which dynamically generates a list of DropDown controls for the user to select from. It doesn’t actually do any of the filtering, that responsibility is handled by the LinqDataSource.
The LinqDataSource is a new control in ASP.NET 3.5 which allows the developer to specify in markup the DataContext, table, and query parameters of a LinqQuery. Other databound controls, such as GridView, DetailsView, and FormView can then reference the datasource. I was surprised to find that the LinqDataSource only supported where parameters where the searching field and value must be equal. In addition you can only AND logic between parameters in the where clause, there is no ability to do OR. There is an option to type in a parameterized clause into the Where property and then specify the names of the parameters, but this is a dynamic data project and that doesn’t sound very dynamic, does it?
No worries, Reflector was to the rescue. I popped open reflector and dug into the System.Web.Extensions 3.6 3.6 dll. First thing I noticed was their extensive use of yield statement’s which does not make for easily read code. Secondly, I saw that the ASP.NET development team did an excellent job of separating the LinqDataSource UI control, from the LinqDataSourceView which actually creates the query. After digging around a while, I found it was possible to replace the LinqDataSourceView using reflection.
The current architecture of the LinqDataSource and the Parameters puts all of the querying logic into the LinqDataSourceView. This makes it extremely hard to extend and add additional capabilities. I suggest a new design which passes the query creation responsibility on to the individual parameters. This can be accomplished through the following interface:
public interface IDynamicExpressionParameter
{
IQueryable AppendDynamicQuery(IQueryable query);
}
Two of the three DynamicExpressionParameters can use the System.Linq.Dynamic.DynamicQuery class. This class isn’t in the .NET Framework but you can find it in the examples installed with Visual Studio 2008, C:Program FilesMicrosoft Visual Studio 9.0Samples1033CSharpSamples.zipLinqSamplesDynamicQuery. There are several excellent examples in here including a ExpressionTreeVisualizer.
This is an example of the RangeExpressionValidator.
public class RangeExpressionParameter : DynamicExpressionParameterBase
{
[DefaultValue("")]
public string MinValue { get; set; }
[DefaultValue("")]
public string MaxValue { get; set; }
public override IQueryable AppendDynamicQuery(IQueryable query)
{
if (!string.IsNullOrEmpty(MinValue) && !string.IsNullOrEmpty(MaxValue))
return query.Where(string.Format("{0} >= @0 AND {0} <= @1", this.Name), new object[] { MinParameterValue, MaxParameterValue });
else if (!string.IsNullOrEmpty(MinValue))
return query.Where(string.Format("{0} >= @0", this.Name), new object[] { MinParameterValue });
else if (!string.IsNullOrEmpty(MaxValue))
return query.Where(string.Format("{0} <= @0", this.Name), new object[] { MaxParameterValue });
else
return query;
}
private object MinParameterValue
{
get
{
return GetValue(MinValue, null, Type, this.ConvertEmptyStringToNull, false);
}
}
private object MaxParameterValue
{
get
{
return GetValue(MaxValue, null, Type, this.ConvertEmptyStringToNull, false);
}
}
}
Here is some example markup from the aspx pages of both a standard Web site and a Dynamic Data Extensions Web Site.
<cc1:DynamicLinqDataSource ID="DynamicLinqDataSource1" ContextTypeName="BusinessObjects.AdventureWorksDataContext"
TableName="Products" runat="server" EnableDelete="True" EnableInsert="True" EnableUpdate="True">
<WhereParameters>
<cc1:RangeExpressionParameter Name="ListPrice" MinValue="10" MaxValue="500" Type="Decimal" />
<cc1:InExpressionParameter Name="Class" ValueType="String" ValueList="L,M" />
<cc1:LikeExpressionParameter Name="Color" Like="StartsWith" Value="B" />
</WhereParameters>
</cc1:DynamicLinqDataSource>
These screen shots show the above expressions in action. The DynamicLinqDataSource works exactly like the standard LinqDataSource with the addition of the Expression Parameters.
I am attaching the source code for the solution to this point. My next steps are to extend the ExpressionParameter to create a CompositeExtension parameter which accepts AND / OR operations between parameters which will allow for even more complex search criteria.









June 19th, 2009 at 11:04 am
[...] part 1 of this series I discussed extending the LinqDataSource to provided advanced querying capabilities [...]