Marcin On ASP.NET

Keeping my eye on the dot

Posts
  • Marcin On ASP.NET

    Building custom LINQ expressions made easy with DynamicQueryable.

    • 0 Comments

    A colleague of mine recently called me out on the fact that I haven't blogged in … oh about a year and a half. Well it’s 2010 now and resolution season is in full swing so here’s my attempt at getting back on the ball (no promises though).

    In a recent post Scott Hanselman described writing custom filters for ASP.NET Dynamic Data. Scott provides a nice overview of the Dynamic Data architecture and then gets his hands dirty with some custom filters using the advanced capabilities available in Dynamic Data Futures. However, he soon realizes that writing custom late-bound LINQ expressions is definitely not a trifle.

    I started thinking about how this experience could be improved and so started writing some code. Things were going great and I was well on my way towards providing the “magic” code that would make Scott’s task that much easier when I started getting this déjà vu feeling that I had already seen something very similar. I scratched my head a bit and then remembered a long-lost sample for dynamically building LINQ queries written back when the original Visual Studio 2008 shipped.

    It’s available as a download but it also comes bundled with your VS (both 2008 and 2010) installation under the following path in your VS installation directory:

    Samples\1033\CSharpSamples.zip\LinqSamples\DynamicQuery\DynamicQuery

    This sample implements a subset of the LINQ extension methods in a late-bound, string-based manner. So for example (using Scott’s model classes) instead of writing this:

    IQueryable<Brick> bricks; // strongly-typed collection of bricks
    var result = bricks.Select(p => p.Year).Distinct().OrderBy(i => i);

    you can write this “magic” code:

    using System.Linq.Dynamic; // you need to include this namespace
                               // for the extension methods to kick in
    
    IQueryable bricks; // weakly-typed collection of bricks
    var result = bricks.Select("Year").Distinct().OrderBy("it");

    DynamicQueryable is quite powerful and includes the following

    • Dynamic string-based querying of any LINQ provider (late-bound versions of Where, Select, OrderBy, Take, Skip, GroupBy, Any, and Count extension methods)
    • String-based mini expression language (like the “it” identifier in the sample above), including complex conditional statements and all operators
    • Dynamic creation of classes for projections

    The only custom code that I had to write to support Scott’s scenario was the Distinct method, which is missing from what’s provided. Fortunately this was not that difficult:

    public static class DynamicQueryableExtras {
        public static IQueryable Distinct(this IQueryable q) {
            var call = Expression.Call(typeof(Queryable), 
                                       "Distinct",
                                       new Type[] { q.ElementType },
                                       q.Expression);
            return q.Provider.CreateQuery(call);
        }
    }

    Other missing LINQ APIs could be added quite easily too.

    Once I had the Distinct extension method it was easy to rewrite Scott’s code:

    protected void Page_Init(object sender, EventArgs e) {
        var items = Column.Table.GetQuery();
        var entityParam = Expression.Parameter(Column.Table.EntityType, "row");
    
        // row => row.Property
        var columnLambda = Expression.Lambda(Expression.Property(entityParam, Column.EntityTypeProperty), entityParam);
    
        // Items.Select(row => row.Property)
        var selectCall = Expression.Call(typeof(Queryable),
                                         "Select",
                                         new Type[] { items.ElementType, columnLambda.Body.Type },
                                         items.Expression,
                                         columnLambda);
    
        // Items.Select(row => row.Property).Distinct
        var distinctCall = Expression.Call(typeof(Queryable),
                                           "Distinct",
                                           new Type[] { Column.EntityTypeProperty.PropertyType },
                                           selectCall);
    
        // colvalue => colvalue
        var sortParam = Expression.Parameter(Column.EntityTypeProperty.PropertyType, "sortValue");
        var columnResultLambda = Expression.Lambda(sortParam, sortParam);
    
        // Items.Select(row => row.Property).Distinct.OrderBy(colvalue => colvalue)
        var ordercall = Expression.Call(typeof(Queryable),
                                        "OrderBy",
                                        new Type[] { Column.EntityTypeProperty.PropertyType, columnResultLambda.Body.Type },
                                        distinctCall,
                                        columnResultLambda);
    
        var result = items.Provider.CreateQuery(ordercall);
    
        foreach (var item in result) {
            if (item != null) DropDownList1.Items.Add(item.ToString());
        }
    }

    as:

    protected void Page_Init(object sender, EventArgs e) {
        var items = Column.Table.GetQuery();
    
        var result = items.Select(Column.EntityTypeProperty.Name)
                          .Distinct()
                          .OrderBy("it");
    
        foreach (var item in result) {
            if (item != null) DropDownList1.Items.Add(item.ToString());
        }
    }

    Much simpler, wouldn’t you agree?

    I hope this technique will save you time trying to figure out how to dynamically build LINQ expressions. The sample contains a detailed document describing all of the APIs and the features available in the expression language. You should definitely check it out because it is very powerful. Oh, and if this also looks familiar to you, that’s because it’s almost the same as the querying features in LinqDataSource.

  • Marcin On ASP.NET

    Dynamic Data Futures 7/16 update posted

    • 1 Comments

    We have just posted an updated version of Dynamic Data Futures on codeplex. The Dynamic Data Features project is a combination of a class library and sample website that showcases some of the future work that will be coming to Dynamic Data. It also contains some workarounds for existing Dynamic Data bugs and issues.

    Here's a rough list of what is new from last week:

  • Added ImprovedDynamicValidator control that fixes issues with DynamicValidator correctly catching exceptions thrown off a validated data model object. A tag mapping in web.config is used to automatically replace all instance of DynamicValidator with ImprovedDynamicValidator. This validator is also now added to the ForeignKey_Edit template.
  • Added EnumDataTypeAttribute that can be used to mark integral columns as actually representing enumerated CLR types.
  • Modified the Enumeration field template and filter template to take EnumDataTypeAttribute into account.
  • Modified the Enumeration filter template to detect if an enum is in flags mode and display a CheckBoxList instead of a DropDownList.
  • Added validation to the DbImage_Edit template. It now verifies that the provided file is a valid image and also that a file is provided at all if the column is required.
  • Added constraints to routes to illustrate how to block invalid requests.
  • Marcin On ASP.NET

    Dynamic Data Futures 7/2 update posted

    • 2 Comments

    We have just posted an updated version of Dynamic Data Futures on codeplex. The Dynamic Data Features project is a combination of a class library and sample website that showcases some of the future work that will be coming to Dynamic Data. It also contains some workarounds for existing Dynamic Data bugs and issues.

    Here's a rough list of what is new from last week:

  • Added DynamicHyperLink control. It generates links to Dynamic Data tables for all actions and works both in data-binding and declarative scenarios. 
  • Added Url and EmailAddress field templates. 
  • Modified ForeignKey_Edit field template to display a [Not Set] entry for required columns in insert mode. Added a RequiredFieldValidator to ensure that an entry is selected if the column is required.
  • Modified routes in Global.asax to generate pretty URLs using the new PrettyDynamicDataRoute.
  • Changed FilterAttribute.Order property to match the behavior of ColumnOrderAttribute.Order property. Now each filter has a default Order of 0; negative numbers can be used to push filters to the front, while positive numbers can be used to push filters to the back.
  • Marcin On ASP.NET

    Dynamic Data Futures 6/25 update posted

    • 2 Comments

    Update: the Dynamic Data Futures was updated on 7/2.

    We have just posted an updated version of Dynamic Data Futures (known for the last 10 days as Dynamic Data Extensions) on codeplex. The Dynamic Data Features project is a combination of a class library and sample website that showcases some of the future work that will be coming to Dynamic Data. It also contains some workarounds for existing Dynamic Data bugs and issues.

    Here's a rough list of what is new from last week:

    • Moved/renamed a number of library files for better logical organization. Classes are now organized what they generally do instead of how they apply to a particular sample.

    • Updated DbImage.ascx to better handle compound and GUID primary key tables. Also, page templates will disable AJAX partial rendering when editing an image column to allow the FileUpload control to work properly.

    • Added support for complex where parameters. This can be seen in the pages under ComplexWhereParameters in the sample app that already have a Where clause for LinqDataSource but also use the filters to contribute extra components.

    • Added automatic support for enumerated type columns. UIHint is no longer needed to reference Enumeration.ascx.

    • Moved a number of extension/utility methods to the DynamicDataFutures class. This includes localization helpers, metadata helpers, and default value helpers.

    • Consolidated IAutoFieldGenerator implementations into a single AdvancedFieldGenerator class. It now includes functionality enabling column ordering, programatically excluding a list of columns, and a workaround for issues with attribute localization.

    • Refactored common LINQ Expression generation code into the LinqExpressionHelper class.

  • Marcin On ASP.NET

    Dynamic Data Features 6/25 update posted

    • 0 Comments

    Update: I made a typo. This project is called Dynamic Data Futures, not Dynamic Data Features. Hope no confusion was caused by this. Please read more about it here.

  • Page 3 of 5 (25 items) 12345