Josh Heyse

Thoughts Defragmented

Archive for the 'C#' Category

Dictionary & Collection Initializers

Posted by jheyse on 18th June 2008

I’ve been working with the 3.5 Framework pretty much exclusively for last 3 months and I just recently ran across one of the new features that I some how missed, Dictionary & Collection Initializers.

Before the C# 3.0 the only collections you could initialize in-line where arrays.  This always left me doing things like:

List<string> list = new List<string>(new string[] { "Cubs", "Sox" });

Now, you can use the same syntax to declare non-array collections as well as dictionarys. Declaring the same collection now looks like this:

List<string> list = new List<string>() { "Cubs", "Sox" };

Collection initializers are great and all, but using this on dictionarys is really cool especially when I am prototyping abstract service factories and I don’t want to have to declare a static method to initialize the dictionary.  I wrote this piece of code today and was amazed at how clean it looked:

public static class ServiceFactory
{
    private static Dictionary<Type, Type> ServiceDictionary = new Dictionary<Type, Type>()
    {
        {typeof(IValidateAddress), typeof(USPS.AddressValidationService)},
        {typeof(ICityStateLookup), typeof(USPS.CityStateLookupService)}
    };
 
    public static TService CreateService<TService>()
        where TService : IService
    {
        TService service = (TService)Activator.CreateInstance(ServiceDictionary[typeof(TService)]);
        service.Initialize();
        return service;
    }
}

Posted in .NET, C# | No Comments »

A Richer DynamicDataFilter – Part 5 ColumnContains

Posted by jheyse on 23rd May 2008

I’m not really sure what to call this post, so it’s just the name of the control I wrote.  A reader left me the following message on a previous post during this series on the extended DynamicDataFilter:

Hi

I was wondering if you could guide me on how to do the following:

On every List page, I need a dropdownlist populated with  all the column names from the Table. Adjacent to that should be a textbox where the user can enter some text. And then a ‘Search’ button.

When clicked on this button, the following should happen:

Perform search on the Table where dropdownlist.selectedcolumnname CONTAINS textbox.text.

Repopulate Gridview with the search results.

I’d appreciate any help or atleast an idea on how to go about this. I’m new to Dynamic Data

Abby, unfortunately the functionality you are looking for isn’t currently supported in DynamicData, the main issue is that the LinqDataSource does not understand CONTAINS the where parameters you supply are all equals.  But, the DynamicData Filtering I have written does support this. (My code isn’t production quality, and should only be used in production at your own risk.)  Here is a quick screen shot:

ColumnContains

To allow for this I created a ColumnContains control which has a DrowDownList of the columns available in the DataSource’s Table and a TextBox which accepts your CONTAINS value.

<table>
    <tr>
        <td>
            <asp:DropDownList ID="ddlColumn" runat="server">
            </asp:DropDownList>
        </td>
        <td>
            <asp:TextBox ID="tbText" runat="server"></asp:TextBox>
        </td>
    </tr>
</table>

The code behind for the control is responsible for binding the list of string based columns to the DropDownList and creates the LikeExpressionParameter which is responsible for the LIKE query.

public partial class ColumnContains : Catalyst.Web.DynamicData.FilterTemplateUserControlBase
{
    protected void Page_Init(object sender, EventArgs e)
    {
        ddlColumn.DataTextField = "Name";
        ddlColumn.DataSource = Table.Columns.Where(c => c.TypeCode == TypeCode.String);
        ddlColumn.DataBind();
    }

    private MetaTable Table
    {
        get
        {
            IDynamicDataSource source = this.FindDataSourceControl();
            if(source != null)
                return source.GetTable();
            return null;
        }
    }

    public override IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource)
    {
        yield return new LikeExpressionParameter()
            {
                Name = ddlColumn.SelectedValue,
                Like = LikeExpressionParameter.LikeType.Contains,
                Value = tbText.Text
            };
    }

    public MetaColumn Column
    {
        get
        {
            return Table.GetColumn(ddlColumn.SelectedValue);
        }
        set
        {
            ddlColumn.SelectedIndex = -1;
            if (value != null)
            {
                ListItem li = ddlColumn.Items.FindByValue(value.Name);
                if (li != null)
                    li.Selected = true;
            }
        }
    }

    public string Value
    {
        get
        {
            return tbText.Text;
        }
        set
        {
            tbText.Text = value;
        }
    }

    public override void LoadQueryStringParameters(NameValueCollection parameters)
    {
        string columnName = parameters[string.Format("{0}_column", this.ID)];
        Column = Table.GetColumn(columnName);
        Value = parameters[string.Format("{0}_value", this.ID)];
    }

    public override NameValueCollection SaveQueryStringParameters()
    {
        NameValueCollection collection = new NameValueCollection();
        collection.Add(string.Format("{0}_column", this.ID), Column.Name);
        collection.Add(string.Format("{0}_value", this.ID), Value);
        return collection;
    }

    public override void Clear()
    {
        Column = null;
        Value = string.Empty;
    }
}

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

Encapsulation Generation Tool

Posted by jheyse on 21st May 2008

Last week I was helping out on a project and there was a need to encapsulate or wrap an existing type to extend some functionality.  The reason was that functionality in the soon to be encapsulated type could not be extended due to a majority of the members being final, or non-overridable.  Wrapping a class can be an extremely tedious exercise, especially if there are many public members.  I am disappointed too see that this isn’t one of the refactoring tools built into Visual Studio. 

EncapsulateToolI thought that this would be a good exercise to familiarize myself with CodeDom and Visual Studio integration. The CodeDom, while complicated isn’t that bad; VS integration on the other hand is a serious pain, so I have decided to forgo the second half of the excessive and the functionality will have to remain in a stand alone tool.

Here is a screen shot of the finished application.  It allows you specify a type to encapsulate, target language and your new class and namespace.

I am not going to bore you with the details of the CodeDom code, it’s pretty easy to follow and I am attaching the code example. 

 

 

 

There are two limitations to the tool:

  • it does not wrap events. In an ideal world the wrapper class would declare the event and delegate the add/remove accessors to the encapsulated class. CodeDom does not supporte the add/remove accessors.
  • it does not wrap properties with indexers, I believe CodeDom supports this, I didn’t bother to figure out how to do this.

There you go Ben.

Download Solution – Encapsulate.zip

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

Safe Binding using Lambdas

Posted by jheyse on 19th May 2008

Friday I was discussing some of the new features in Visual Studio 2008 SP1 with some co-workers and one of them mentioned a new feature where VS 2008 will do symbolic renaming on all WCF Service Artifacts.  This lead to a conversation on how the IDE understands properties and methods differently then their corresponding names in XML configuration files (WCF) and string literals (data binding).  During the conversation I had a small light come on, regarding data binding and string literals. 

With .NET 3.5 it is possible to do compile time checking for data binding using Lambda expressions.  The idea is that instead of using a string to represent the name of the property you would pass in a Lambda Expression referencing that property.  Here is a simple example using a business object property that raises PropertyChanged.

public class Person : INotifyPropertyChanged
{
    private string _name;
 
    public string Name
    {
        get { return _name; }
        set
        {
            if (!object.Equals(_name, value))
            {
                _name = value;
                OnPropertyChanged(() => Name);
            }
        }
    }
 
    private void OnPropertyChanged(Expression<Func<object>> property)
    {
        MemberExpression me = property.Body as MemberExpression;
        if (me == null)
            throw new ArgumentException("property.Body must be of type MemberExpression", "property");
 
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(me.Member.Name));
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
}

The first benefit you get by doing this is that when you use the VS IDE refractoring tools to rename Name, the IDE will find Name in the Lambda expression and rename it there as well.  It is also possible to create type safe implementation using a generic base class:

public class BusinessBase<TObject> : INotifyPropertyChanged
{
    protected bool SetHelper<T>(Expression<Func<TObject, object>> property, ref T field, T value)
    {
        MemberExpression me = property.Body as MemberExpression;
        if (me == null)
            throw new ArgumentException("property.Body must be type MemberExpression", "property");
 
        if (!object.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(me.Member.Name);
            return true;
        }
        return false;
    }
 
    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
 
}

and it’s corresponding implementation:

public class Person : BusinessBase<Person>
{
 
    private string _name;
 
    public string Name
    {
        get { return _name; }
        set { SetHelper<string>(p => p.Name, ref _name, value); }
    }
 
}

Honestly, I am not crazy about the second implementation which requires inheriting from a generic class and giving it a concrete implementation, which locks you into always being BusinessBase<Person> when you’ve extended Person to be Employee.  I will have a serious of blogs posts on some of these issues, with my Core series soon.

So anyway, enough with the business objects, lets look at databinding. Currently in .NET you create a binding between a control and an object using the following code:

this.firstTextBox.DataBindings.Add(new Binding("Text", this.personBindingSource, "First", true));

This loosely couples the Text property of the firstTextBox TextBox to the First property of some object, referenced by personBindingSource.  Once the designer generates this code, it is not checked against the actually objects.  Using Lambda Expressions the previous line of code could be implemented like:

this.firstTextBox.DataBindings.Add(
    new ExpressionBinding<TextBox, Person>(c => c.Text, this.personBindingSource, p => p.First, true));

where the definition of ExpressionBinding extends Binding:

public class ExpressionBinding<TControl, TDataSource> : Binding
    {
        public ExpressionBinding(Expression<Func<TControl, object>> property, object dataSource, Expression<Func<TDataSource, object>> dataMember)
            : this(property, dataSource, dataMember, false, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
        { }
 
        public ExpressionBinding(Expression<Func<TControl, object>> property, object dataSource, Expression<Func<TDataSource, object>> dataMember, bool formattingEnabled)
            : this(property, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
        { }
 
        public ExpressionBinding(Expression<Func<TControl, object>> property, object dataSource, Expression<Func<TDataSource, object>> dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode)
            : this(property, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null)
        { }
 
        public ExpressionBinding(Expression<Func<TControl, object>> property, object dataSource, Expression<Func<TDataSource, object>> dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue)
            : this(property, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, string.Empty, null)
        { }
 
        public ExpressionBinding(Expression<Func<TControl, object>> property, object dataSource, Expression<Func<TDataSource, object>> dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString)
            : this(property, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, null)
        { }
 
        public ExpressionBinding(Expression<Func<TControl, object>> property, object dataSource, Expression<Func<TDataSource, object>> dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo)
            : base((property.Body as MemberExpression).Member.Name, dataSource, (dataMember.Body as MemberExpression).Member.Name, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, formatInfo)
        { }
    }

Unfortunately, this implementation is not practical in WinForms since the current Visual Studio designer does not support lambda expressions for binding.   I would be interested to see if the WinForms team is looking into improved implementations of data binding with the 3.5 technologies. 

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

A Richer DynamicFilterRepeater – Updated for April Release

Posted by jheyse on 11th April 2008

This week the Dynamic Data team has released a new drop of the runtime and templates.  It is substantially different and they have made many improvements to the design.  These changes, as expected, broke a significant portion of the Dynamic Filter controls I have been working on.  Last night I dug into the new bits and was able to breath life back into the dynamic filtering. 

Once I had finished doing symbolic renames on many of the properties and types renamed from the Decmeber CTP, I ran into several issues with the reflection based code I have written to inject the advanced querying capabilities into the existing controls.  I am going to start documenting what I have had to ‘hack’ to make the dynamic querying work.  Hopefully I can work with team members within Microsoft to promote some of the methods and types to the public. 

Here are some screen shots of the new implementation:

DD_4

DD_5

Download Solution: DynamicData.zip

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

A Richer DynamicFilterRepeater – Part 4 Custom DynamicFilterControls & Expressions

Posted by jheyse on 11th April 2008

In post 2 in this series I contrived a scenario where composite parameters were used to select products from the AdventureWorks database by weight.  The weight of products is stored in either lbs or grams which make it necessary for the following SQL logic:

select
    *
from
    Production.Product
where
    (WeightUnitMeasureCode = 'LB' and Weight >= 2 and Weight <= 3) or
    (WeightUnitMeasureCode = 'G' and Weight >= 907 and Weight <= 1306)

Previously, this was accomplished by compounding a serious of And, Or, and Range expressions together.  We can condense this down to a single custom range parameter and corresponding FilterUserControl.

The first step is to create a user control which extends RangeFilterUserControlBase.  We will name it Weight and place it in the ~/App_Shared/DynamicDataFilters/ folder.  The control will allow for values in both Grams and Pounds to support globalization, so we need to add a UnitType property.  The ascx for the control defines tbMin, tbMax, and a DropDownList to allow the user to select either Pounds or Grams.  The control extends RangeFilterUserControlBase and overrides the default implementation of GetWhereParameters and returns a WeightExpressionParameter which we will define next.

public partial class Weight : Catalyst.Web.DynamicData.RangeFilterUserControlBase
{
    public enum WeightUnit
    {
        Pounds,
        Grams
    }

    public WeightUnit UnitType
    {
        get
        {
            return (WeightUnit)Enum.Parse(typeof(WeightUnit), DropDownList1.SelectedValue);
        }
        set
        {
            DropDownList1.Items.FindByValue(value.ToString()).Selected = true;
        }
    }

    public override string MinValue
    {
        get
        {
            return tbMin.Text;
        }
        set
        {
            tbMin.Text = value;
        }
    }

    public override string MaxValue
    {
        get
        {
            return tbMax.Text;
        }
        set
        {
            tbMax.Text = value;
        }
    }

    public override TypeCode Type
    {
        get { return TypeCode.Decimal; }
    }

    public override IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource)
    {
        yield return new WeightExpressionParameter()
        {
            Name = this.DataField,
            MinValue = this.MinValue,
            MaxValue = this.MaxValue,
            UnitType = this.UnitType,
            Type = this.Type
        };
    }
}

The WeightExpressionParameter extends the RangeExpressionParameter and overrides the GetLambaExpression to return a LambdaExpression which will do the Grams to Pounds conversion for us.  The WeightExpressionParameter also includes a property for the UnitType which is stored in ViewState to ensure post backs and Ajax enabled controls work correctly.

public class WeightExpressionParameter : RangeExpressionParameter
{
    private const decimal ConversionFactor = 453.59237m;

    public WeightUnit UnitType
    {
        get
        {
            object obj2 = this.ViewState["UnitType"];
            if (obj2 == null)
            {
                return WeightUnit.Pounds;
            }
            return (WeightUnit)obj2;
        }
        set
        {
            if (this.UnitType != value)
            {
                this.ViewState["UnitType"] = value;
                this.OnParameterChanged();
            }
        }
    }

    public override LambdaExpression GetLambdaExpression(Type itType)
    {
        decimal? min = (decimal?)MinParameterValue;
        decimal? max = (decimal?)MaxParameterValue;

        if (UnitType == WeightUnit.Grams)
        {
            min /= ConversionFactor;
            max /= ConversionFactor;
        }

        if(itType == typeof(BusinessObjects.Product))
        {
            Expression<Func<BusinessObjects.Product, bool>> x = p =>
                (min == null || (p.WeightUnitMeasureCode == "lb" ? p.Weight : p.Weight / ConversionFactor) >= min) &&
                (max == null || (p.WeightUnitMeasureCode == "lb" ? p.Weight : p.Weight / ConversionFactor) <= max);
            return x;
        }
        else
            throw new ArgumentException("Expected itType to be of type BusinessObjects.Product", "itType");
    }
}

The LINQ-to-SQL interpreter is amazingly smart and is able to understand fairly complex statements. This SQL that is passed to the database server is exactly what we are looking for, it is even able to short circuit the query if either min or max is null!

SELECT TOP (10) [t0].[ProductID], [t0].[Name], [t0].[ProductNumber], [t0].[MakeFlag], [t0].[FinishedGoodsFlag], [t0].[Color], [t0].[SafetyStockLevel], [t0].[ReorderPoint], [t0].[StandardCost], [t0].[ListPrice], [t0].[Size], [t0].[SizeUnitMeasureCode], [t0].[WeightUnitMeasureCode], [t0].[Weight], [t0].[DaysToManufacture], [t0].[ProductLine], [t0].[Class], [t0].[Style], [t0].[ProductSubcategoryID], [t0].[ProductModelID], [t0].[SellStartDate], [t0].[SellEndDate], [t0].[DiscontinuedDate], [t0].[rowguid], [t0].[ModifiedDate], [t2].[test], [t2].[ProductSubcategoryID] AS [ProductSubcategoryID2], [t2].[ProductCategoryID], [t2].[Name] AS [Name2], [t2].[rowguid] AS [rowguid2], [t2].[ModifiedDate] AS [ModifiedDate2], [t4].[test] AS [test2], [t4].[ProductModelID] AS [ProductModelID2], [t4].[Name] AS [Name3], [t4].[CatalogDescription], [t4].[Instructions], [t4].[rowguid] AS [rowguid3], [t4].[ModifiedDate] AS [ModifiedDate3]
FROM [Production].[Product] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[ProductSubcategoryID], [t1].[ProductCategoryID], [t1].[Name], [t1].[rowguid], [t1].[ModifiedDate]
    FROM [Production].[ProductSubcategory] AS [t1]
    ) AS [t2] ON [t2].[ProductSubcategoryID] = [t0].[ProductSubcategoryID]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t3].[ProductModelID], [t3].[Name], [t3].[CatalogDescription], [t3].[Instructions], [t3].[rowguid], [t3].[ModifiedDate]
    FROM [Production].[ProductModel] AS [t3]
    ) AS [t4] ON [t4].[ProductModelID] = [t0].[ProductModelID]
WHERE ((
    (CASE
        WHEN [t0].[WeightUnitMeasureCode] = @p0 THEN CONVERT(Decimal(29,5),[t0].[Weight])
        ELSE [t0].[Weight] / @p1
    END)) >= @p2) AND ((
    (CASE
        WHEN [t0].[WeightUnitMeasureCode] = @p3 THEN CONVERT(Decimal(29,5),[t0].[Weight])
        ELSE [t0].[Weight] / @p4
    END)) <= @p5)
-- @p0: Input NVarChar (Size = 2; Prec = 0; Scale = 0) [lb]
-- @p1: Input Decimal (Size = 0; Prec = 29; Scale = 5) [453.59237]
-- @p2: Input Decimal (Size = 0; Prec = 34; Scale = 5) [2]
-- @p3: Input NVarChar (Size = 2; Prec = 0; Scale = 0) [lb]
-- @p4: Input Decimal (Size = 0; Prec = 29; Scale = 5) [453.59237]
-- @p5: Input Decimal (Size = 0; Prec = 34; Scale = 5) [3]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8

Here is a screen shot of the control in action:

WeightFilterControl

Weight Filter Control Example: Weight.zip

Posted in .NET, ASP.NET, C#, Dynamic Data, LINQ | No Comments »

A Richer DynamicFilterRepeater – Part 3 DynamicFilterControl

Posted by jheyse on 10th April 2008

In part 1 of this series I discussed extending the LinqDataSource to provided advanced querying capabilities using the System.Linq.Dynamic namespace.  Part 2 demonstrated creating advanced WhereParameters which allowed for for complex binary logic.  In this posting I am going to go through our first ASP.NET Web Control which brings this powerful functionality to the user.

My original intention of this blog series was to create a more powerful DynamicFilterControl to replace the one currently in the Dynamic Data portion of the ASP.NET 3.5  Extensions.  During a recent project where we use the Dynamic Data framework, it became apparent that the repeater limited our layout ability and a DynamicFilterForm was better suited.  I choose the name DynamicFilterForm to follow the naming convention of the ASP.NET databound controls.

So, let’s start off with a demo of the the DynamicFilterForm.  In this demo I am building on the Products  example used in previous postings.  For the List.aspx page we’d like to be able to filter on the fields displayed in the DataGrid.

DynamicFilterForm_1[5]

The search allows for all of the fields, except weight, to be search on.  The Name, Product Number, Class and Color allow for partial string matches, ListPrice allows for a range, Subcategory uses a single selection drop down which is populated through the ForiegnKey, and Product Model allows for multiple selection.

The DynamicFilterForm allows for 3 commands: Search, Clear, and Browse.  (More on these later).

Let’s take a look at the ASP.NET code that runs the form:

<cc1:DynamicFilterForm DataSourceID="GridDataSource" runat="Server" ID="DynamicFilterForm1">
    <FilterTemplate>
        <div>
            Search
        </div>
        <table>
            <tr>
                <td>
                    Name:
                </td>
                <td>
                    <cc1:DynamicFilterControl ID="DynamicFilterControl3" runat="server" DataField="Name"
                        FilterMode="Contains" />
                </td>
                <td>
                    Product Number:
                </td>
                <td>
                    <cc1:DynamicFilterControl ID="DynamicFilterControl5" runat="server" DataField="ProductNumber"
                        FilterMode="Contains" />
                </td>
                <td>
                    Class:
                </td>
                <td>
                    <cc1:DynamicFilterControl ID="DynamicFilterControl6" runat="server" DataField="Class"
                        FilterMode="Contains" />
                </td>
                <td rowspan="2" valign="top">
                    Product Model:
                </td>
                <td rowspan="2" valign="top">
                    <cc1:DynamicFilterControl ID="DynamicFilterControl7" runat="server" DataField="ProductModel" FilterMode="MultiSelect" />
                </td>
                <td rowspan="2" valign="top">
                    <asp:LinkButton runat="server" CommandName="Search">Search</asp:LinkButton><br />
                    <asp:LinkButton runat="server" CommandName="Clear">Clear</asp:LinkButton><br />
                    <asp:LinkButton runat="server" CommandName="Browse">Browse</asp:LinkButton>
                </td>
            </tr>
            <tr>
                <td>
                    List Price:
                </td>
                <td>
                    <cc1:DynamicFilterControl ID="DynamicFilterControl1" runat="server" DataField="ListPrice"
                        FilterMode="Range" />
                </td>
                <td>
                    Color:
                </td>
                <td>
                    <cc1:DynamicFilterControl ID="DynamicFilterControl4" runat="server" DataField="Color"
                        FilterMode="Contains" />
                </td>
                <td>
                    Subcategory:
                </td>
                <td>
                    <cc1:DynamicFilterControl ID="DynamicFilterControl2" runat="server" DataField="ProductSubcategory" />
                </td>
            </tr>
        </table>
    </FilterTemplate>
</cc1:DynamicFilterForm>

DynamicFiltersFolder

The DynamicFilterForm mimics the functionality of the other DynamicData controls.  The DynamicFilterForm is a container object which has a FilterTemplate template that defines the layout.  Inside the FilterTemplate you define DynamicFilterControls.  These controls are responsible for loading the correct user control from the ~/App_Shared/DynamicDataFilters/ folder.  Here you can see I have created filter controls for each of the field types declared in the DynamicDataFields folder which are included in the base solution.

The DynamicFilterControl has a property FilterMode which allows for the following different enumeration values: Equals, Contains, Range, Multiselect.  This mode is appended to the field type like the DynamicControls.  So if you want to allow for a range of integers, the control name would be Integer_Range.ascx.  To facilitate quicker development and code reuse each of the custom DynamicExpressionParameters (EqualsExpressionParameter, InExpressionParameter, etc…) has a corresponding FilterTemplateUserControlBase(EqualsFilterUserControlBase, InFilterControlUserBase, etc…).

Here are the abstract methods for the FilterTemplateUserControlBase which need to be implemented.

public abstract class FilterTemplateUserControlBase : FieldTemplateUserControlBase, IWhereParametersProvider
{
    public abstract IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource);
    public abstract void LoadQueryStringParameters(NameValueCollection parameters);
    public abstract NameValueCollection SaveQueryStringParameters();
    public abstract void Clear();

}

GetWhereParameters is the meat of this class, it is what is responsible for instantiating and returning the ExpressionParameter which is added to the DynamicLinqDataSource control’s where parameters.  Don’t you love the separation of concerns? I DO! The Clear method is called by the parent DynamicFilterForm when the Clear command is issued, this clears all values and researches to bring back the unfiltered result set.  LoadQueryStringParameters and SaveQueryStringParameters allow for the filter criteria to be stored in a URL.

<rant> One of my biggest annoyances on the web is developers who store state in session or form variables around searching.  Web users expect to be able to copy the URL of the page they are on and send it to their friend so that they can see the same thing. I run into this every time my friends and I are trying to plan a trip someplace and we are using Orbitz, Travelocity, etc… </rant>

Here is an example of the RangeFilterUserControlBase and the Integer_Range.ascx.cs control which extends it.

public abstract class RangeFilterUserControlBase : FilterTemplateUserControlBase
{
    public abstract string MinValue { get; set; }
    public abstract string MaxValue { get; set; }
    public abstract TypeCode Type { get; }

    public override IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource)
    {
        yield return new RangeExpressionParameter() { Name = DataField, MinValue = MinValue, MaxValue = MaxValue, Type = Type };
    }

    public override void LoadQueryStringParameters(NameValueCollection parameters)
    {
        MinValue = parameters[string.Format("{0}_Min", DataField)];
        MaxValue = parameters[string.Format("{0}_Max", DataField)];
    }

    public override NameValueCollection SaveQueryStringParameters()
    {
        NameValueCollection parameters = new NameValueCollection();
        parameters.Add(string.Format("{0}_Min", DataField), MinValue);
        parameters.Add(string.Format("{0}_Max", DataField), MaxValue);
        return parameters;
    }

    public override void Clear()
    {
        MinValue = string.Empty;
        MaxValue = string.Empty;
    }
}

The Integer_Range.ascx control has two TextBoxes named tbMin and tbMax.

public partial class Integer_Range : Catalyst.Web.DynamicData.RangeFilterUserControlBase
{
    public override string MinValue
    {
        get
        {
            return tbMin.Text;
        }
        set
        {
            tbMin.Text = value;
        }
    }

    public override string MaxValue
    {
        get
        {
            return tbMax.Text;
        }
        set
        {
            tbMax.Text = value;
        }
    }

    public override TypeCode Type
    {
        get { return this.MetaMember.TypeCode; }
    }
}

As always, below is the source to the solution so far.  In my next post I will demonstrate creating your own FilterUserControl by re-implementing filtering on the Weight column.

Download Solution: DynamicData.zip

Posted in .NET, ASP.NET, C#, Dynamic Data, LINQ | No Comments »

Updated ASP.NET Dynamic Data Drop

Posted by jheyse on 10th April 2008

Scott Guthrie just posted about an updated version of the ASP.NET Dynamic Data framework.  I am going to look into this and investigate if there were any breaking changes to the Dynamic Filter control set.  I’m guessing there will be several since I am being a bad kid and using reflection to call non-public methods. LOL

Posted in .NET, ASP.NET, C#, Dynamic Data, LINQ | No Comments »

A Richer DynamicFilterRepeater: Part 2 – Advanced Parameters

Posted by admin on 5th April 2008

After extending the LinqDataSource in Part 1, I was curious to see what other types of parameters I could make.  My first idea was to create composite parameters for and/or operations.  The existing implementation of the LinqDataSource only allows you to append filter criteria to the list, which creates an AndAlso functionality.

Continuing with the AdventureWorks Products example, I devised a scenario where composite parameters would be useful. The Products in the AdventureWorks database allow you to store the weight of the item in either lbs or grams. This requires the use of a composite parameter to select all products which fall in between 2 – 3 lbs or the equivalent 907 – 1360 grams.  Since I have been playing with WPF and Silverlight I have taken a liking to the declarative syntax. The following query looked like this in my head:

<cc1:DynamicLinqDataSource ID="GridDataSource" runat="server">
    <WhereParameters>
        <cc1:OrExpressionParameter>
            <Parameters>
                <cc1:AndExpressionParameter>
                    <Parameters>
                        <cc1:EqualsExpressionParameter Name="WeightUnitMeasureCode" Value="LB" />
                        <cc1:RangeExpressionParameter Name="Weight" MinValue="2" MaxValue="3" Type="Int32" />
                    </Parameters>
                </cc1:AndExpressionParameter>
                <cc1:AndExpressionParameter>
                    <Parameters>
                        <cc1:EqualsExpressionParameter Name="WeightUnitMeasureCode" Value="G" />
                        <cc1:RangeExpressionParameter Name="Weight" MinValue="907" MaxValue="1360" Type="Int32" />
                    </Parameters>
                </cc1:AndExpressionParameter>
            </Parameters>
        </cc1:OrExpressionParameter>
    </WhereParameters>
</cc1:DynamicLinqDataSource>

Once I had the use case for the feature completed it was time to refactor my code to support the new functionality.  As I am writing this I am kicking myself for not creating some corresponding UnitTests for this little project.  If I have some time I will write up some tests before I release the code base to the public.

Anyway… To support the change in functionality I had to make several changes:

  • Modify IDyanmicExpressionParameter
  • Extend ParameterCollection
  • Create CompositeExpressionParameterBase

First, I had to modify the IDynamicExpressionParameter interface to support an additional method called GetLambdaExpression.  The reason for this is that we can’t just append to the query anymore, the composite parameters must compile their own binary tree of Lambda Expressions to work correctly.

public interface IDynamicExpressionParameter
{
    IQueryable AppendQuery(IQueryable query);

    LambdaExpression GetLambdaExpression(Type itType);
}

Next, I needed to extend the ParameterCollection to accept parameters beyond the built in ones with the .NET Framework.  Under my previous implementation parameters were not being tracked in ViewState and this was causing an issue with postbacks.  The DynamicParameterCollection accepts additional Parameter types, but still uses a hard coded list.  A better implementation would be to create a static list of knownTypes that appends new knownTypes as they are found.

public class DynamicParameterCollection : ParameterCollection
{
    private readonly Type[] _knownTypes = new Type[] { typeof(AndExpressionParameter),
                                                        typeof(OrExpressionParameter),
                                                        typeof(InExpressionParameter),
                                                        typeof(LikeExpressionParameter),
                                                        typeof(RangeExpressionParameter),
                                                        typeof(EqualsExpressionParameter) };

    protected override object CreateKnownType(int index)
    {
        int baseTypesLength = base.GetKnownTypes().Length;
        if(index < baseTypesLength)
            return base.CreateKnownType(index);

        return Activator.CreateInstance(_knownTypes[index - baseTypesLength]);
    }

    protected override Type[] GetKnownTypes()
    {
        List<Type> knownTypes = new List<Type>(base.GetKnownTypes());
        knownTypes.AddRange(_knownTypes);
        return knownTypes.ToArray();
    }
}

Lastly, I implemented CompositeExpressionParameterBase which supports both And and Or binary tree operations.  There were two key pieces to this class.

The first was wiring up the IStateManager interface to work properly with the parent DynamicParamterCollection.  This proved fairly difficult to debug before I remembered that I could now step into the .NET Framework using Visual Studio 2008. This was my first experience doing so, and I think it is a HUGE feature. The VS team rocks!

The next step was to implement binary trees as Lambda Expressions.  This forced me to get a little deeper into Lamdba Expressions and the ExpressionTreeVisualizer example paid off big time.  You can find the Debug Visualizer in the %PROGRAM FILES%Microsoft Visual Studio 9.0Samples1033CSharpSamples.zip directory.

LinqExpressionVisualizer

The code for creating a binary tree expression looks likes this:

protected LambdaExpression GetLambdaExpression(Type itType, ExpressionType binaryType)
{
    List<LambdaExpression> lambdas = new List<LambdaExpression>();
    foreach (Parameter p in Parameters)
    {
        LambdaExpression lambda = p.GetLambdaExpression(itType);
        if (lambda != null)
            lambdas.Add(lambda);
    }

    if (lambdas.Count == 0)
        return null;

    if (lambdas.Count == 1)
        return lambdas[0];

    LambdaExpression left = lambdas[0];
    LambdaExpression right = lambdas[1];

    ParameterExpression param = Expression.Parameter(left.Parameters[0].Type, "");
    BinaryExpression root = Expression.MakeBinary(binaryType, left.Body, right.Body);
    for (int i = 2; i < lambdas.Count; i++)
    {
        right = lambdas[i];
        root = Expression.MakeBinary(binaryType, root, right.Body);
    }

    left = Expression.Lambda(ReplaceParameter(root, param), param);
    return left;
}

Some other ideas for advanced parameters include a NotNullOrEmpty parameter and a Not parameter which inverses the result of the composite expression.  Up next I will talk about a writing web controls to expose this advance functionality to users.

Download Solution DynamicData.zip

Posted in .NET, ASP.NET, C#, 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 »