Playing with Extension Methods: .ToDataTable();

written by Andrew Tobin on Wednesday, March 19 2008

Today at work Mabster and I were discussing Mocking Frameworks after viewing Scott Hanselman's MVC videos.

Then Matt brought up that he had heard about this Moq framework that uses Lambdas to do some mocking and wanted to try it out.

So he played with it for a while and was trying to get an SqlReader mocked up completely so he could mock a call back to a database, obviously without a database.

He ran into a bit of trouble trying to mock up an IDataReader the way he wanted and asked around online for a bit of a hand, he needed a way to get a sample DataTable set up easily.

Anyway, I thought why couldn't he just somehow define one from a List?  Being fairly new to the world of .NET, but having read a bit, I thought it wouldn't be too hard - until he mentioned there wasn't a straightforward way to do that.

So I started playing around thinking that maybe there could be an easy way to make an extension method to convert a List to a DataTable, and ended up getting all into Generics and Reflection to get it sorted.

I got a fairly basic version to prove the concept and tonight Matt and I have had another dig into it, and with Matt's help we've got a fairly good implementation for a .ToDataTable() with lambdas, and things doing most of the work for us.

This won't work with nested tables at the moment, but should allow anything that implements IEnumerable to change into a DataTable, if you ever feel the need!

       public static DataTable ToDataTable<T>(this IEnumerable<T> list)
       {
           var table = new DataTable();

           if (list.Count() == 0) return table;

           var objectType = list.First().GetType();

           foreach (var t in objectType.GetProperties().Where(p => p.CanRead && p.GetIndexParameters().Length == 0))
           {
               var col = table.Columns.Add(t.Name);
               col.ExtendedProperties["Type"] = "P";
               col.DataType = t.PropertyType; 
           }

           foreach (var t in objectType.GetFields().Where(f => f.IsPublic))
           {
               var col = table.Columns.Add(t.Name);
               col.ExtendedProperties["Type"] = "F";
               col.DataType = t.FieldType;
           }

           foreach (var listRow in list)
           {
               var tableRow = table.NewRow();
               foreach (DataColumn col in table.Columns)
               {
                   if (col.ExtendedProperties["Type"].ToString() == "F")
                   {
                       var p = objectType.GetField(col.ColumnName);
                       if (p != null)
                       {
                           tableRow[col.ColumnName] = p.GetValue(listRow);
                       }
                   }
                   else if (col.ExtendedProperties["Type"].ToString() == "P")
                   {
                       var p = objectType.GetProperty(col.ColumnName);
                       if (p != null)
                       {
                           tableRow[col.ColumnName] = p.GetValue(listRow, null);
                       }
                   }
               }
               table.Rows.Add(tableRow);
           }

           return table;
       }

This will even work with Anonymous Type list being passed into it like the following:

            var myTable = (
                new[] { 
                    new { FirstName = "Bob", LastName = "Smith", Stuff = new { Age = 80 } }, 
                    new { FirstName = "John", LastName = "Doe", Stuff = new { Age = 32 } }             
                }).ToDataTable();

We also got one working that you can pass the columns that you want returned, if you only want a few columns from your class list back:

        public static DataTable ToDataTable<T>(this IEnumerable<T> list, params string[] columns)
        {
            var table = new DataTable();

            if (list.Count() == 0) return table;

            var objectType = list.First().GetType();

            foreach (var column in columns)
            {
                foreach (var t in objectType.GetProperties().Where(p => p.CanRead && p.GetIndexParameters().Length == 0 && p.Name == column))
                {
                    var col = table.Columns.Add(t.Name);
                    col.ExtendedProperties["Type"] = "P";
                    col.DataType = t.PropertyType;
                }

                foreach (var t in objectType.GetFields().Where(f => f.IsPublic && f.Name == column))
                {
                    var col = table.Columns.Add(t.Name);
                    col.ExtendedProperties["Type"] = "F";
                    col.DataType = t.FieldType;
                }
            }
            foreach (var listRow in list)
            {
                var tableRow = table.NewRow();
                foreach (DataColumn col in table.Columns)
                {
                    if (col.ExtendedProperties["Type"].ToString() == "F")
                    {
                        var p = objectType.GetField(col.ColumnName);
                        if (p != null)
                        {
                            tableRow[col.ColumnName] = p.GetValue(listRow);
                        }
                    }
                    else if (col.ExtendedProperties["Type"].ToString() == "P")
                    {
                        var p = objectType.GetProperty(col.ColumnName);
                        if (p != null)
                        {
                            tableRow[col.ColumnName] = p.GetValue(listRow, null);
                        }
                    }
                }
                table.Rows.Add(tableRow);
            }

            return table;
        }

And you can call that like this:

            var myTable = (
                new[] { 
                    new { FirstName = "Bob", LastName = "Smith", Stuff = new { Age = 80 } }, 
                    new { FirstName = "John", LastName = "Doe", Stuff = new { Age = 32 } }             
                }).ToDataTable("Stuff", "FirstName");

I'm not sure how much use a DataTable might be for most people, but it was a great little skills project to learn a bit more about C# and it may help us out mocking some more projects in the future!

Anyway, thanks to Matt for the help, and I hope someone gets something out of this!

Similar Posts

  1. Just Added Live Messenger to my Blog
  2. SQL Code Camp - Day 1
  3. Code Camp Oz 2008

Post a comment

Options:

Size

Colors