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;

                }

 

 

 

        }

}



 

7 Comments

  • This class doesn't support any properties that are Nullable.

  • This article is great. Easy to modify to make it work with Nullable.

    While checking for the SystemType, also check for Nullable.

    if( IsNullableType( pi.PropertyType ) ) {
    type = type.GetGenericArguments()[0];
    }

    private static bool IsNullableType( Type theType ) {
    return ( theType.IsGenericType && theType.
    GetGenericTypeDefinition().Equals
    ( typeof( Nullable ) ) );
    }

    Filling the Dataset should be straight forward.

  • I can't get this to work. I have an array of an object. The object is a class with 23 properties (string, bool and Guid). The array has 560 rows.

    I get "A column named 'SyncRootLength' already belongs to this DataTable" at dt.Columns.Add(dc).

    So I modified the column name line to

    dc.ColumnName = expandedName + pi.Name + Guid.NewGuid().ToString();

    This result in a stack overflow, obviously from the recursive call to CreateColumns. But is 560 elements really that large?

    Thanks.

  • Also ich vermute das ist nur ne Modeerscheinung

  • Unglaublich! Sowas hätte ich gar nicht geglaubt :)

  • Hat jemand eine Idee wie sehr dies verallgemeinerbar ist?

  • Tjo, die Dinge können so trivial erscheinen. Besten Dank für die Erläuterungen ;)

Comments have been disabled for this content.