Thursday, April 4, 2013

Using Extensions to Add Behavior

I discussed .NET extensions in a previous article and provided some useful examples. I've lately considered other fine uses for this feature that promise to make code not only simpler, but more readable and more self-documented. That's the real value of extensions. You can write the same code without them and your application will work just as well. But by using extensions, you have the opportunity to make your code more easily maintained.

Here's a great example:

I was recently coding an application that required a context menu for a DataGridView which operated on the selected row. Simple enough? Well, not if you expect a right-click to select the row in addition to showing the context menu. As it turns out, this does not work that way in the WinForms DataGridView. To make it work, I had to select the row in code during the MouseUp even when a right-click occurred.


    this.dataGridView1.MouseUp += (o, m) =>
    {
        DataGridView.HitTestInfo hti = this.dataGridView1.HitTest(m.X, m.Y);
        if (m.Button == MouseButtons.Right && hti.RowIndex != -1)
            if (!grid.Rows[hti.RowIndex].Selected)
            {
                this.dataGridView1.ClearSelection();
                this.dataGridView1.Rows[hti.RowIndex].Selected = true;
            }
    };


That's not too bad, but it would be messy if I needed to do this a lot. 
It sure would have been nice if there were a property I could set for this behavior. Extensions won't let you create properties, but the simplicity of a property can be achieved. What I'd like to do, then, is to call a method like so:
 
this.fileListDataGridView.SelectRowOnRightClick();
 

That's must better. Well, I can easily accomplish this will an extension:


public static class DataGridViewBehaviors
{
    public static void SelectRowOnRightClick(this DataGridView grid)
    {
        grid.MouseUp += (a, m) =>
        {
            DataGridView.HitTestInfo hti = grid.HitTest(m.X, m.Y);
            if (m.Button == MouseButtons.Right && hti.RowIndex != -1)
                if (!grid.Rows[hti.RowIndex].Selected)
                {
                    grid.ClearSelection();
                    grid.Rows[hti.RowIndex].Selected = true;
                }
        };
    }
}


Notice that I chose to call the class containing this extension DataGridViewBehaviors. Although this is not all that different from extensions that act on an object, such as GetSelectedRows(), conceptually this is a little different. I'm using the extension to configure the behavior of an object - a behavior that I'll likely want for other objects of the same type elsewhere.

Moreover, I can make my code much more readable because the method name is clear and concise. Making code readable is an important part of good coding and essential for maintaining that code.

Wednesday, January 23, 2013

Using Extensions to Make Code Simpler

Extension Methods were a new feature as of .NET 3.0. They are used extensively in Linq to SQL and have become one of my favorite tools. The premise is quite simple, as the C# Programming Guide explains:

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.

The main reason to use extensions is to make code more readable and to reuse logic, especially when dealing with classes that you cannot change. For example, consider the following snippet for the get and set accessors of a ComboBox in a Windows Form application:


public List<StateDataView> StateList
{
    get
    {
        List<StateDataView> states = (List<StateDataView>)this.statesComboBox.DataSource;
        states.RemoveAt(0);
        return states;
    }
    set
    {
        List<StateDataView> states = new List<StateDataView>(value);
        states.Insert(0, new StateDataView() { Code = "" });
        this.statesComboBox.DataSource = states;
    }
}
public StateDataView SelectedState
{
    get
    {
        if (this.statesComboBox.SelectedIndex == 0) return null;
        return (StateDataView)this.statesComboBox.SelectedItem;
    }
}

In the Set accessor, I did a little trick to make sure there was a blank entry in the list. This would be useful when created a lookup form that has a variety of options that may not always be used. When returning the selected object in the Get accessor, I have to handle the case when the blank entry is selected, in which case I want to return a null. This code isn't too bad, but imagine how ugly it would look if I had a lot of ComboBoxes. Wouldn't it be great if the could could be written like this?

public List<StateDataView> StateList
{
    get { return this.statesComboBox.GetDataSourceWithoutBlank<StateDataView>(); }
    set { this.statesComboBox.SetDataSourceWithBlank<StateDataView>(value); }
}
public StateDataView SelectedState
{
    get { return this.statesComboBox.GetSelectedItemUnlessFirst<StateDataView>(); }
}

Notice that there are three methods I am calling on the ComboBox control that do not exist in the framework. These are my own extension methods. It should be fairly clear by their names what they do.

If I only had one or two cases like this, I wouldn't bother with the extension method. However, I find this particular scenario to be quite common. Let's look at the extension methods that make it happen. They exist in a class I created specifically to extend the Windows Form ComboBox:

public static class ComboBoxExtensions
{
    public static void SetDataSourceWithBlank<T>(this ComboBox combo, List<T> list)
            where T : new()
    {
        List<T> newlist = new List<T>(list);
        newlist.Insert(0, new T());
        combo.DataSource = newlist;
    }
    public static List<T> GetDataSourceWithoutBlank<T>(this ComboBox combo)
    {
        List<T> list = (List<T>)combo.DataSource;
        list.RemoveAt(0);
        return list;
    }
    public static T GetSelectedItemUnlessFirst<T>(this ComboBox combo)
        where T : class
    {
        if (combo.SelectedIndex == 0) return null;
        return (T)combo.SelectedItem;
    }
}

There are a few important characteristics of extension methods:
  • They must exist in a static class.
  • They must themselves be static.
  • The first parameter must be prefixed with the keywork this and must be the type being extended.
I made liberal use of generics in this case, which is fairly ubiquitous in C# development these days. Because of that, I had to clarify a couple things for my methods:
  • Since I needed to create a new instance of an object in the SetDataSourceWithBlank method, I had to use the new() type constraint.
  • Since I needed to return a null in the GetSelectedItemUnlessFirst method, I had to use the class constraint.
I find using Extension Methods especially helpful in the MVP pattern because the view is mostly a set of properties that expose controls. The logic involved in setting the control properties and determining certain details, such as whether or not something is selected, is a concern of the view, so it is appropraite for the logic to be maintained there. By using extension methods, you can build a useful library that will not only make your code easier to read, it will make it easier to maintain and easier to write.

Some Useful Extensions

For the remainder of this article, I thought I'd share a few extensions that I've created that you might find useful.

GridView.SelectWhere<T>

Description: This method allows you to select rows in a DataGridView based on the properties of the bound object. I typically use this to select a specific row, but it could also be used to select a set or rows.

Usage: grid.SelectWhere<type>(x => x.property == value);

public static void SelectWhere<T>(this DataGridView grid,
     Expression<Func<T,bool>> testexpression)
{
    grid.ClearSelection();
    Func<T,bool> test = testexpression.Compile();
    foreach (DataGridViewRow row in grid.Rows)
    {
        T item = (T)row.DataBoundItem;
        if (test(item))
        {
            row.Selected = true;
        }
    }
}

IEnumerable<T>.Distinct

Description: Gets a distinct list of objects from an IEnumerable based on a filter expression. In this case, the key must be a nullable integer, but Icould easily create different overloads for different types.

Usage: list.Distinct(x => x.filterproperty);

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source,
     Expression<Func<T,int?>> key)
{
    List<T> items = new List<T>();
    Func<T,int?> keyprop = key.Compile();
    foreach (T sourceitem in source.ToList())
    {
        int? id = keyprop(sourceitem);
        if (items.Find(i => keyprop(i) == id) == null)
            items.Add(sourceitem);
    }
    return items.AsEnumerable();
}

Nullable<T>.Coalesce

Description: Just like the T-SQL equivalent, this method gives you an alternative value to return when a nullable type is null, e.g. if you want a nullable int to return a -1 when it's null.

Usage: value.Coalesce(x);
 
public static T Coalesce<T>(this Nullable<T> value, T nullvalue)
{
    if (value.HasValue) return value.Value;
    return nullvalue;
}

Thursday, January 10, 2013

LINQ to SQL Ad-Hoc Searches

One of the nicest features about LINQ to SQL is that it is type safe. It won't compile if I misspell something or have a conversion error. This makes using LINQ to SQL very efficient. However, when queries get complicated, it often becomes very difficult to get the correct query - and to get an optimal query can absolutely impossible. In a previous article, Practical Thoughts on Using ORMs, I recommended created database views whenever queries got complex. But sometimes the complexity is not about the joining, but about the criteria. In this article, I'd like to demonstrate a method for doing ad-hoc searches much like you'd find in Google or any search engine.

Suppose you needed an ad-hoc search interface for the following class:

public class Customer
{
      public string Id { get; set; }
      public string AccountName { get; set; }
      public string AccountNumber { get; set; }
      public string StateCode { get; set; }
}

The users just want to type something that would be used as criteria against multiple fields, such as a list of words. By itself, that may be simple enough. You decide which properties to query against, split the list of words, and create multiple Where statements like so:

IQueryable<Customer> query = from c in Customers
                                select c;

foreach (string criterion in criteria.Split(' '))
{
     query = query.Where(c =>
                    c.AccountName.Contains(criterion) ||
                    c.AccountNumber.Contains(criterion) ||
                    c.StateCode.Contains(criterion));
}

return query.ToList();

 
The problem with this code, however, is that the criteria in the Where clause will all be joined with And conditions. It would be more intuitive if they used OR conditions. Unfortunately, that is not supported in LINQ to SQL out of the box.
 
Never fear, a number of helpful coders have provided an extension library to solve that problem: LINQKit. You can download the binaries and source code at
What we're most interested in is the PedicateBuilder. There's an example of just what we're doing here in the link above; however, we're going to get a little more complicated.
After referencing the LINQKit assembly and namespace, we get a couple useful extensions. First, there are extension methods for And and Or on Expression<Func<T,bool>>, which is what we're using in the Where clause. Second, in order to make it work, we need another extension method called AsExpandable (note, the documentation suggests this is only necessary for EntityFramework, as opposed to straight Linq-to-SQL.) With these two extensions, our code can be modified thusly:

IQueryable<Customer> query = from c in Customers
                                select c;
Expression<Func<Customer, bool>> searchclause;
foreach (string criterion in criteria.Split(' '))
{
        Expression<Func<Customer, bool>> predicate = c =>
                                        c.AccountName.Contains(criterion) ||
                                        c.AccountNumber.Contains(criterion) ||
                                        c.StateCode.Contains(criterion));
    searchclause = (searchclause==null) ? predicate : searchclause.Or(predicate);
}
query = query.Where(searchclause);
return query.AsExpandable().ToList();
That's great for a simple ad-hoc search form. However, what if I want to provide some more elaborate features, such as explicit AND operator or a string of text in quotes or a NOT operator? To do that, I need to be more sophisticated with the criteria string and how I parse it.
The logic involved is fairly simple but maybe not so obvious; it will make sense once you work through it, though. Below is a complete code for what I just described:
IQueryable<Customer> query = from c in Customers
                               select c;

Expression<Func<Customer, bool>> searchclause = null;

bool useOrCondition = true;
bool useNotCondition = false;

foreach (string criterion in criteria.Split(' '))
{
    string term = criterion;
    if (term.Trim().Length == 0)
    {
        // do nothing
    }
    else if (term.ToUpper() == "AND")
    {
        useOrCondition = false;
    }
    else if (term.ToUpper() == "OR")
    {
        useOrCondition = true;
    }
    else if (term.ToUpper() == "NOT")
    {
        useNotCondition = true;
        useOrCondition = false;
    }
    else
    {
        Expression<Func<Customer, bool>> predicateexpression;
        if (useOrCondition)
        {
            predicateexpression = c =>
                c.AccountNumber.Contains(term) ||
                c.AccountName.Contains(term) ||
                c.StateCode.Contains(term);
            searchclause = (searchclause == null)
                ? predicateexpression : searchclause.Or(predicateexpression);
        }
        else if (useNotCondition)
        {
            predicateexpression = c =>
                c.AccountNumber.Contains(term) &&
                c.AccountName.Contains(term) &&
                c.StateCode.Contains(term);
            searchclause = (searchclause == null)
                ? predicateexpression : searchclause.And(predicateexpression);
        }
        else
        {
            predicateexpression = c =>
                c.AccountNumber.Contains(term) ||
                c.AccountName.Contains(term) ||
                c.StateCode.Contains(term);
            searchclause = (searchclause == null)
                ? predicateexpression : searchclause.And(predicateexpression);
        }

        useOrCondition = true;
        useNotCondition = false;
    }
}
query = query.Where(searchclause);

return query;

There are a couple things that this code does NOT do:
1. It does not handled terms surrounded by quotes.
2. It does not handle operator precedence (if you mix AND and OR the results will not likely be what you expect.
3. It does not handle parentheses.
You could probably expand the code above to handle all these cases with a little effort.