Josh Heyse

Thoughts Defragmented

Archive for February, 2008

A Richer DynamicFilterRepeater: Part 1 – Extending the LinqDataSouce

Posted by admin on 27th February 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.

WebPage[1] DynamicData

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. 

Posted in ASP.NET, Dynamic Data, LINQ | 1 Comment »

Yet Another Windows Live Writer Code Plug-in

Posted by admin on 24th February 2008

I took some time this weekend to get our company blog site in order.  I have neglected my blog long enough and I am going to start posting more.  One of my biggest issues with blogging was the poor web user interface we had be using.  So I started digging into the rich client blogging tools.  Once I found out Live Writer had a plug-in model, I was sold over Office 2007.

After several hours of upgrading our Community Server to a version which supported the full MetaWeblog API including media objects I started to look for a Code Plug-in.  There were several ones I liked but they didn’t have the exact features I wanted.

The plug-ins I looked at were:

I like that Mike’s wrapped everything in a pre tag which allowed me to control the style through CSS style sheets.  But Rick’s was hands down much better, because it kept the colors used in Visual Studio as opposed to parsing my code and coloring keywords.  It was fairly simple to add support for classes to Rick’s plug-in.

Rick’s plug-in uses JT Leigh’s Copy as HTML plug in which is an excellently written add-in for Visual Studio.  The one thing that Rick left out of the plug-in was JT Leigh’s dialog for modifying the formatting settings.   The dialog has many options which I fell should be available to Live Writer user.  The plug-in options are available from Tools -> Options -> Plug-ins -> Insert Visual Studio Code -> Options

 LiveWriter

 

I modified the dialog window and formatter to support a user supplied class for the div which wraps the source code.  This gave me the ability to control my div formatting in Community Servers ‘Override CSS’ feature.

Here is the code for the plug-in with the ability to edit the options.

[WriterPluginAttribute("97C8235B-7F91-4f2e-921B-7F77AB515BD5",
        "Paste Code From Visual Studio",
        ImagePath = "Images.PasteCode.png",
        PublisherUrl = "http://blogs.catalystss.com/blogs/josh_heyse/default.aspx",
        Description = "Paste Code From Visual Studio",
        HasEditableOptions = true)]
 
    [InsertableContentSourceAttribute("Visual Studio Code")]
    public class VsCodePlugin : ContentSource
    {
        public override DialogResult CreateContent(IWin32Window dialogOwner, ref string newContent)
        {
            DialogResult result = DialogResult.OK;
            if (System.Windows.Forms.Clipboard.ContainsText() == true)
            {
                Copier rtfImporter = new Copier();
                newContent = rtfImporter.CopyAsHtml(System.Windows.Forms.Clipboard.GetText(TextDataFormat.Rtf)) + "<p></p>rn";
            }
            else
            {
                result = MessageBox.Show(dialogOwner, "Copy your HTML to the clipboard.", "Paste as Html", MessageBoxButtons.OKCancel);
                if (result == DialogResult.OK && System.Windows.Forms.Clipboard.ContainsText() == true)
                {
                    newContent = System.Windows.Forms.Clipboard.GetText();
                }
            }
 
            return result;
        }
 
        public override void EditOptions(IWin32Window dialogOwner)
        {
            (new CopierPreferencesDialog()).ShowDialog(dialogOwner);
        }
    }

I am attaching the source code and plug-in.

Alright, so now I have no more excuses; time to start publishing content. Happy Blogging!

Posted in .NET, C#, Live Writer, Visual Studio | No Comments »