Paul On Technology

Exploring technology

May 2005 - Posts

Converting objects or arrays of objects to DataTables Part II

I have had a chance to work a little more on converting objects to data tables. Oddur has asked about nested types and I have implemented them - to an extent. The issue is how far do I really need to take this? Our needs are simple, but I did run into having to handle enums. I decided that an enum would always produce a String column and that I would use the enum name as the value.

Again, this is a work in progress and I need to refactor but I have it working. Here's the code and if anyone has any ideas then please leave a comment.

 

using System;

using System.Collections;

using System.Data;

using System.Reflection;

using System.Reflection.Emit;

 

namespace ObjToAdo

{

        /// <summary>

        ///             The Converter class provides methods to convert objects into data tables.

        /// </summary>

        public class Converter

        {

                /// <summary>

                ///             An array of the names of the System data types.

                /// </summary>

                private static String[] _systemTypes = new String[]{ "SYSTEM.BYTE", "SYSTEM.CHAR", "SYSTEM.DECIMAL", "SYSTEM.DOUBLE", "SYSTEM.INT16", "SYSTEM.INT32", "SYSTEM.INT64",

                                                                                                                "SYSTEM.SBYTE", "SYSTEM.SINGLE", "SYSTEM.UINT16", "SYSTEM.UINT32", "SYSTEM.UINT64", "SYSTEM.DATETIME",

                                                                                                                "SYSTEM.STRING", "SYSTEM.BOOLEAN" };

 

                private Converter(){}

 

                /// <summary>

                ///             Converts an object into a data table.

                /// </summary>

                /// <param name="source">Spcifies the object to convert into a data table.</param>

                /// <returns>Returns a data table created from an object.</returns>

                public static DataTable ConvertToDataTable(Object source)

                {      

                        PropertyInfo[] properties = source.GetType().GetProperties();

                        DataTable dt = CreateDataTable(properties, source);

                        dt.TableName = source.GetType().Name;

                        FillData(properties, dt, null, String.Empty, source);

                        return dt;

                }

 

 

                /// <summary>  

                ///             Converts an array of objects into a data table.

                /// </summary>

                /// <param name="array">Specifies the array of objects that wil be used to create the data table.</param>

                /// <returns>Returns a data table created from the array of objects.</returns>

                public static DataTable ConvertToDataTable(Object[] array)

                {

                        PropertyInfo[] properties = array.GetType().GetElementType().GetProperties();

                        DataTable dt = CreateDataTable(properties, null);

                        dt.TableName = array.GetType().GetElementType().Name;

 

                        if (array.Length != 0)

                        {

                                foreach(object source in array)

                                        FillData(properties, dt, null, String.Empty, source);

 

                        }

 

                        return dt;

                }

 

 

                /// <summary>

                ///             Creates a new data table from the properties of an object.

                /// </summary>

                /// <param name="properties">Specifies the property information used to create the data table.</param>

                /// <returns>Returns a data table created from an object.</returns>

                private static DataTable CreateDataTable(PropertyInfo[] properties, object source)

                {

                        DataTable dt = new DataTable();

                        CreateColumns(properties, dt, String.Empty, source);

                        return dt;

                }

 

 

                /// <summary>

                ///             Creates the columns in a data table from the properties of an object.

                /// </summary>

                /// <param name="properties">Specifies the property information used to create the columns.</param>

                /// <param name="dt">Specifies the data table being created.</param>

                /// <param name="expandedName">Specifies the property name plus the nested object's property name when the column name is from a nested object.</param>

                private static void CreateColumns(PropertyInfo[] properties, DataTable dt, String expandedName, object source)

                {

                        DataColumn dc = null;

                        PropertyInfo[] nestedProperties = null;

                        Object nested = null;

 

                        foreach(PropertyInfo pi in properties)

                        {                              

                                if (IsSystemType(pi.PropertyType.ToString()) || pi.PropertyType.IsEnum)

                                {

                                        dc = new DataColumn();

                                        dc.ColumnName = expandedName + pi.Name;

 

                                        if ( pi.PropertyType.IsEnum)    // Enums always get the string type because we stuff the enum item name as the value.

                                                dc.DataType = Type.GetType("System.String");

                                        else

                                                dc.DataType = pi.PropertyType;

                               

                                        dt.Columns.Add(dc);                            

                                }

                                else

                                {

                                        nested = pi.GetValue(source, null);

 

                                        if (pi.GetType().IsArray)

                                                nestedProperties = nested.GetType().GetElementType().GetProperties();

                                        else

                                                nestedProperties = nested.GetType().GetProperties();

                                       

                                        CreateColumns(nestedProperties, dt, pi.Name, nested);

                                }

                        }

                }

 

                /// <summary>

                ///             Fills a data row with values from an object.

                /// </summary>

                /// <param name="properties">Specifies the property that we are fill the data from.</param>

                /// <param name="dt">Specifies the data table being fllled.</param>

                /// <param name="row">Specifies the data row that will be filled with the property value.</param>

                /// <param name="expandedName">Specifies the property name plus the nested object's property name when the column name is from a nested object.</param>

                /// <param name="source">Specifies the object that contains the property being read..</param>

                private static void FillData(PropertyInfo[] properties, DataTable dt, DataRow row, String expandedName, object source)

                {

                        DataRow newRow = null;

                        PropertyInfo[] nestedProperties = null;

                        Object nested = null;

 

                        if (row == null)

                                newRow = dt.NewRow();

                        else

                                newRow = row;

 

                        foreach(PropertyInfo pi in properties)

                        {

                                if (IsSystemType(pi.PropertyType.ToString()) )

                                        newRow[expandedName + pi.Name] = pi.GetValue(source, null);

                                else if(pi.PropertyType.IsEnum)

                                {

                                        int i = 0;

                                        object itemVal = pi.GetValue(source, null);

                                        string[] names = Enum.GetNames(itemVal.GetType());                                                                                                             

 

                                        // Pull the enum text into the column value.

                                        foreach(object o in Enum.GetValues(itemVal.GetType()))

                                        {                                              

                                                if (o.ToString() == itemVal.ToString())

                                                        newRow[expandedName + pi.Name] = names[i];                                     

                                                i++;

                                        }

 

 

                                }

                                else

                                {

                                        nested = pi.GetValue(source, null);

 

                                        if (pi.GetType().IsArray)

                                                nestedProperties = nested.GetType().GetElementType().GetProperties();

                                        else

                                                nestedProperties = nested.GetType().GetProperties();

 

                                        FillData(nestedProperties, dt, newRow, pi.Name, nested);

                                }

 

                        }

 

                        if (row == null)

                                dt.Rows.Add(newRow);   

                }

 

 

                /// <summary>

                ///             IsSystemType determines if the name of a proerty type is one of the system data types.

                /// </summary>

                /// <param name="type">Specifies the name of the property type.</param>

                /// <returns>True if the type is a system type, otherwise false.</returns>

                private static bool IsSystemType(String type)

                {

                        String utype = type.ToUpper();

 

                        foreach(String st in _systemTypes)

                        {

                                if (utype == st)

                                        return true;

                        }

 

                        return false;

                }

 

 

 

        }

}



 

Converting objects or arrays of objects to DataTables

Recently there have been some articles about not using DataSets or DataTables in web applications. Scott Mitchell has Why I Don't Use DataSets in My ASP.NET Applications and More On Why I Don't Use DataSets in My ASP.NET Applications. Karl Sequin has On the Way to Mastering ASP.NET: Introducing Custom Entity Classes.

I prefer to use custom objects also, mostly because I like to abstract the database away from the client app. So I have this API with custom classes (fed from DataReaders) and a coworker wants to use my API to drive some reports. The only problem is the tool they are using doesn't work with arrays of objects. They say it needs DataSets, DataTables or DataReaders.

At first I started getting nervous but then I thought about it. DataSets and DataTables are fed from DataReaders. That's when I said to myself, "Self, you can feed DataSets and DataTables from your custom classes by using reflection."

Now I am far from a Reflection guru, but all they need are column names,  data types and values. I can build DataColumns from the property info of classes dynamically using Reflection. The following code is my first pass. If anyone sees a way to improve on this then by all means, let's hear it.

using System;

using System.Data;

using System.Reflection;

using System.Reflection.Emit;

 

namespace ObjToAdo

{

        /// <summary>

        /// Summary description for Converter.

        /// </summary>

        public class Converter

        {

                private Converter()     {}

 

                /// <summary>

                ///

                /// </summary>

                /// <param name="o"></param>

                /// <returns></returns>

                public static DataTable ConvertToDataTable(Object o)

                {      

                        PropertyInfo[] properties = o.GetType().GetProperties();

                        DataTable dt = CreateDataTable(properties);

                        FillData(properties, dt, o);

                        return dt;

                }

 

                /// <summary>

                ///

                /// </summary>

                /// <param name="o"></param>

                /// <returns></returns>

                public static DataTable ConvertToDataTable(Object[] array)

                {

                        PropertyInfo[] properties = array.GetType().GetElementType().GetProperties();

                        DataTable dt = CreateDataTable(properties);

 

                        if (array.Length != 0)

                        {

                                foreach(object o in array)

                                        FillData(properties, dt, o);

 

                        }

 

                        return dt;

                }

 

                /// <summary>

                ///

                /// </summary>

                /// <param name="properties"></param>

                /// <returns></returns>

                private static DataTable CreateDataTable(PropertyInfo[] properties)

                {

                        DataTable dt = new DataTable();

                        DataColumn dc = null;

 

                        foreach(PropertyInfo pi in properties)

                        {

                                dc = new DataColumn();

                                dc.ColumnName = pi.Name;

                                dc.DataType = pi.PropertyType;

                               

                                dt.Columns.Add(dc);                            

                        }

 

                        return dt;

                }

 

 

                /// <summary>

                ///

                /// </summary>

                /// <param name="properties"></param>

                /// <param name="dt"></param>

                /// <param name="o"></param>

                private static void FillData(PropertyInfo[] properties, DataTable dt, Object o)

                {

                        DataRow dr = dt.NewRow();

 

                        foreach(PropertyInfo pi in properties)

                                dr[pi.Name] = pi.GetValue(o, null);

 

                        dt.Rows.Add(dr);       

                }

 

 

        }

}

 

 

 

More Posts