Extending Collections in .NET

Recently I found myself adding custom code to convert the collections to DataViews so that I could assign my filters and let the View take care of the rest. In this article I will try to cover the problem, design, and solution implemented.


Since the introduction of Generics, the .NET framework has seen many different collections of strongly typed objects. The process of binding those collections to a GridView is as simple as setting the source to the collection and calling the bind function. Recently I found myself adding custom code to convert the collections to DataViews so that I could assign my filters and let the View take care of the rest. In this article I will try to cover the problem, design, and solution implemented.

Problem

The problem was briefly stated in the introduction. Basically everytime a collection needed a view I found myself writing code like the one below.

DataTable tbCounties = new System.Data.DataTable();
DataView view;

tbCounties.Columns.Add("CountyID", typeof(int));
tbCounties.Columns.Add("StateID", typeof(int));
tbCounties.Columns.Add("Name", typeof(string));

foreach (County county in counties)
{
    tbCounties.Rows.Add(new object[] { county.CountyID, county.StateID, county.Name });
}

view = new DataView(tbCounties);
view.Sort = sortExpression;

In a small collection like the example above, coding it everytime wouldn't take long. The problem would be when a collection contained ten or more properties or worse yet, that the code would not be reusable for other collections without some work.

Design

The design needed to resolve my problem needed to be simple and extensible. The first thing that came to mind was how to leverage Reflection to do some object exploration and come up with a DataView. Once I had a good idea of what I could retrieve with Reflection and how to use the functionality, the last step was exposing it for all my collections to leverage. The final design decision indicated the delivery of the functinality through and extension method called ToView.

Solution and Implementation

With a clear idea of what the end goal was. The remaining task involved figuring out what type to extend so that all the collections were extended without the method needing to know about any particular type. A common interface found implemented by many of the Generic templates like List<T> is the IList interface.
 

  /// <summary>|
        /// Extend any collection implementing IList to return a DataView.
        /// </summary>
        /// <param name="list">IList (Could be List<Type>)</param>
        /// <returns>DataView</returns>
        public static DataView ToView(this IList list)
        {
            // Validate Source
            if (list.Count < 1)                                             
                return null;

            // Initialize DataTable and get all properties from the first Item in the List.
            DataTable table = new DataTable(list.GetType().Name);
            PropertyInfo[] properties = list[0].GetType().GetProperties();
 
            // Build all columns from properties found. (Custom attributes could be added later)
            foreach (PropertyInfo info in properties)
            {
                try
                {
                    table.Columns.Add(new DataColumn(info.Name, info.PropertyType));
                }
                catch (NotSupportedException)
                {
                    // DataTable does not support Nullable types, we want to keep
underlying type.
                    table.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType)));
                }
                catch (Exception)
                {
                    table.Columns.Add(new DataColumn(info.Name, typeof(object)));
                }
            }

            // Add all rows
            for (int index = 0; index < list.Count; index++)
            {
                object[] row = new object[properties.Length];

                for (int i = 0; i < row.Length; i++)
                {
                    row[i] = properties[i].GetValue(list[index], null); // Get the value for each items property
                }

                table.Rows.Add(row);
            }
 
            return new DataView(table); ;
        }

The listing above contains the entire listing for the extension. Attention was kept to the limitations of a DataTable in regards to Null able types. In the case of a NotSupportedException, it was assumed that we needed the underlying type of a Nullable object instead of casting it to an object.

Closing

The .NET framework cotains many features and functinality that we could combine as developers to solve everyday common problems. Extending a collection to improve functionality may not always work the first time, but braking up the task into different modules will make the task less daunting.

erver'>