15

I created a DataRow on my project:

DataRow datarow;

I want to convert this DataRow to any Type of Object. How could I do it?

Jimmy
  • 2,805
  • 6
  • 43
  • 57
folk
  • 687
  • 3
  • 8
  • 16

11 Answers11

28

This is a pretty cool way I use it.

    public static T ToObject<T>(this DataRow dataRow)
    where T : new()
    {
        T item = new T();

        foreach (DataColumn column in dataRow.Table.Columns)
        {
            PropertyInfo property = GetProperty(typeof(T), column.ColumnName);

            if (property != null && dataRow[column] != DBNull.Value && dataRow[column].ToString() != "NULL")
            {
                property.SetValue(item, ChangeType(dataRow[column], property.PropertyType), null);
            }
        }

        return item;
    }

    private static PropertyInfo GetProperty(Type type, string attributeName)
    {
        PropertyInfo property = type.GetProperty(attributeName);

        if (property != null)
        {
            return property;
        }

        return type.GetProperties()
             .Where(p => p.IsDefined(typeof(DisplayAttribute), false) && p.GetCustomAttributes(typeof(DisplayAttribute), false).Cast<DisplayAttribute>().Single().Name == attributeName)
             .FirstOrDefault();
    }

    public static object ChangeType(object value, Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
        {
            if (value == null)
            {
                return null;
            }

            return Convert.ChangeType(value, Nullable.GetUnderlyingType(type));
        }

        return Convert.ChangeType(value, type);
    }
15

I Have found one solution for my application.

    // function that creates an object from the given data row
    public static T CreateItemFromRow<T>(DataRow row) where T : new()
    {
        // create a new object
        T item = new T();

        // set the item
        SetItemFromRow(item, row);

        // return 
        return item;
    }

    public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
    {
        // go through each column
        foreach (DataColumn c in row.Table.Columns)
        {
            // find the property for the column
            PropertyInfo p = item.GetType().GetProperty(c.ColumnName);

            // if exists, set the value
            if (p != null && row[c] != DBNull.Value)
            {
                p.SetValue(item, row[c], null);
            }
        }
    }

This will map your DataRow to ViewModel, Like below.

Your_ViewModel model = CreateItemFromRow<Your_ViewModel>(row);
Bharat
  • 5,869
  • 4
  • 38
  • 58
  • 2
    Worked for me, although I got conflicts in my data model between integer types i.e. Int32 vs Int64, even when I thought I shouldn't. Changing them all to Int64 did the trick but I'm hoping to find the cause. – codah Aug 10 '17 at 02:22
  • 1
    I'm still using it. Just that I'm going through a big ramp-up moving from C++ to C#, so I'm grabbing snippets from all over with little time to delve into some of these issues. Appreciate the code, as a C# noob! – codah Aug 10 '17 at 05:05
  • I have lots of mapping like this, let me know if you need anything else.. – Bharat Aug 10 '17 at 07:24
  • @Bharat, or someone, what is needed in the viewmodel to get this line to work: PropertyInfo p = item.GetType().GetProperty(c.ColumnName); – pdschuller Jun 13 '18 at 22:30
  • 1
    @pdschuller your viewModel will contain same property and column name that you have in your table, it's case sensitive. so you have to make sure whatever property you want to map from table, it should be there in ViewModel. – Bharat Jun 14 '18 at 03:42
  • @Bharat, thanks. Now a really dumb question. I can do foreach(DataRow dr in DataTable.Rows) { but how do I call CreateItemFromRow ? Thanks. – pdschuller Jun 20 '18 at 22:30
  • @pdschuller I can give you exact answer if I can see your code, can you create separate question with full detail and paste link of that question here? I will guide you there... – Bharat Jun 21 '18 at 03:49
  • 2
    Aaaaugh. Its there in your original post. Your_ViewModel model = CreateItemFromRow(row); – pdschuller Jun 21 '18 at 11:18
  • @pdschuller +1 for Aaaaugh! – ourmandave Jun 23 '20 at 16:02
  • 1
    Brilliant! Thank you for this – EE_Kraig Dec 08 '20 at 14:53
7
class Person{
public string FirstName{get;set;}
public string LastName{get;set;}
}

Person person = new Person();
person.FirstName = dataRow["FirstName"] ;
person.LastName = dataRow["LastName"] ;

or

Person person = new Person();
person.FirstName = dataRow.Field<string>("FirstName");
person.LastName = dataRow.Field<string>("LastName");
Avi Fatal
  • 1,550
  • 8
  • 8
6

Similar to some of the previous approaches, I created this extension method for DataRow which takes an argument object to be populated. Main difference is that in addition to populating object's Properties, it also populates Fields of given object. This should also work for simpler structures (Though I only tested on objects).

public static T ToObject<T>( this DataRow dataRow )
     where T : new() {
    T item = new T();
    foreach( DataColumn column in dataRow.Table.Columns ) {
        if( dataRow[column] != DBNull.Value ) {
            PropertyInfo prop = item.GetType().GetProperty( column.ColumnName );
            if( prop != null ) {
                object result = Convert.ChangeType( dataRow[column], prop.PropertyType );
                prop.SetValue( item, result, null );
                continue;
            }
            else {
                FieldInfo fld = item.GetType().GetField( column.ColumnName );
                if( fld != null ) {
                    object result = Convert.ChangeType( dataRow[column], fld.FieldType );
                    fld.SetValue( item, result );
                }
            }
        }
    }
    return item;
}

You can put this code in your current class or in a global static class. It needs following namespaces...

using System;
using System.Data;
using System.Reflection;

Usage is as simple as...

MyClassName obj = dataRow.ToObject<MyClassName>()
EMalik
  • 2,446
  • 1
  • 26
  • 13
5

Here is an extension method that would allow you to convert a DataRow to a given object.

public static class DataRowExtensions
{
    public static T Cast<T>(this DataRow dataRow) where T : new()
    {
        T item = new T();

        IEnumerable<PropertyInfo> properties = item.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                                             .Where(x => x.CanWrite);

        foreach (DataColumn column in dataRow.Table.Columns)
        {
            if (dataRow[column] == DBNull.Value)
            {
                continue;
            }

            PropertyInfo property = properties.FirstOrDefault(x => column.ColumnName.Equals(x.Name, StringComparison.OrdinalIgnoreCase));

            if (property == null)
            {
                continue;
            }

            try
            {
                Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

                object safeValue = (dataRow[column] == null) ? null : Convert.ChangeType(dataRow[column], t);

                property.SetValue(item, safeValue, null);
            }
            catch
            {
                throw new Exception($"The value '{dataRow[column]}' cannot be mapped to the property '{property.Name}'!");
            }

        }

        return item;
    }
}

And you can use the above extension method like so

foreach (DataRow row in dataTable.Rows)
{
    SomeClassType obj = row.Cast<SomeClassType>();
    // do something with your object
}
2

Given Converter<TIn, TOut> is a delegate, then the following should work:

List<Person> personList = new List<Person>();

personList = ConvertDataRowToList(ds, (row) => {
    return new Person
    {
        FirstName = row["FirstName"],
        LastName  = row["LastName"]
        // Rest of properties should assign here...
    };
});

https://learn.microsoft.com/en-us/dotnet/api/system.converter-2

Walter Stabosz
  • 7,447
  • 5
  • 43
  • 75
Thilina H
  • 5,754
  • 6
  • 26
  • 56
1

Apart from the manual method Avi shows, you can use a mapping system like AutoMapper to do the transformation for you. This is particularly useful in the case where you have a lot of columns/properties to map.

Check out this article on how to use AutoMapper to convert a DataTable to a list of objects.

Corey
  • 15,524
  • 2
  • 35
  • 68
  • N.B. automapper can't handle it if the source type and destination types differ, when using DataReaderMapper – Jack Ukleja Apr 22 '16 at 02:12
  • @Schneider Since the question is explicitly about converting a `DataRow` to another object type the problem doesn't arise. – Corey Apr 22 '16 at 02:15
  • But DataRow is 'untyped'. Automapper won't be able to infer what to map to what, and so you are back to square one. OP may then investigate the AutoMappers IDataReader mapping capability which *does* do clever stuff to convert DataRows into strong types, and this is what my original comment informs on – Jack Ukleja Apr 22 '16 at 02:26
0

DataRow has a property ItemArray, which contains an array of object values. You can work with this array and create any custom type with the values from your DataRow.

IDeveloper
  • 1,249
  • 2
  • 13
  • 22
0

With less complications ;), two steps will solve the task: 1. cast to dictionary (ToDictionary). 2. map dictionary to entity (MapToEntity).

    public static IDictionary<string, object> ToDictionary(
        this DataRow content
        )
    {
        var values = content.ItemArray;
        var columns = content
            .Table
            .Columns
            .Cast<DataColumn>()
            .Select(x => x.ColumnName);
        return values
            .Select((v, m) => new { v, m })
            .ToDictionary(
                x => columns.ElementAt(x.m)
                , x => (x.v == DBNull.Value ? null : x.v)
             );
    }
    public static T MapToEntity<T>(
        this IDictionary<string, object> source
        )
        where T : class, new()
    {
        // t - target
        T t_object = new T();
        Type t_type = t_object.GetType();

        foreach (var kvp in source)
        {
            PropertyInfo t_property = t_type.GetProperty(kvp.Key);
            if (t_property != null)
            {
                t_property.SetValue(t_object, kvp.Value);
            }
        }
        return t_object;
    }

...and the usage would be:

DataRow dr = getSomeDataRow(someArgs);
ABC result = dr.ToDictionary()
  .MapToEntity<ABC>();
Sam Saarian
  • 992
  • 10
  • 13
0

You could convert the whole Data table into a list Object like the code below. Of course, you can take the specific object which you want with the index or the field value.

    /// <summary>
    /// convert a datatable to list Object
    /// </summary>
    /// <typeparam name="T">object model</typeparam>
    /// <param name="dataTable"></param>
    /// <returns>ex ussage: List<User> listTbl = CommonFunc.convertDatatblToListObj<User>(dataTable);</returns>
    public static List<T> convertDatatableToListObject<T>(DataTable dataTable)
    {
        List<T> res = new List<T>();
        try
        {
            string tblJson = JsonConvert.SerializeObject(dataTable);

            res = JsonConvert.DeserializeObject<List<T>>(tblJson);
        }
        catch (Exception ex)
        {
            string exStr = ex.Message;
        }
        return res;
    }
Coden
  • 2,579
  • 1
  • 18
  • 25
-1

With these changes worked fine for me, for fields int, long, int? and long?

// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
    // create a new object
    T item = new T();

    // set the item
    SetItemFromRow(item, row);

    // return 
    return item;
}

public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
    // go through each column
    foreach (DataColumn c in row.Table.Columns)
    {
        // find the property for the column
        PropertyInfo p = item.GetType().GetProperty(c.ColumnName);

        // if exists, set the value
        if (p != null && row[c] != DBNull.Value)
        {
            if (p.PropertyType.Name == "Int64")
            {
                p.SetValue(item, long.Parse(row[c].ToString()), null);
            }
            else if (p.PropertyType.Name == "Int32")
            {
                p.SetValue(item, int.Parse(row[c].ToString()), null);
            }
            else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int32"))
            {
                p.SetValue(item, (int?)int.Parse(row[c].ToString()), null);
            }
            else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int64"))
            {
                p.SetValue(item, (long?)long.Parse(row[c].ToString()), null);
            }
            else
            {
                p.SetValue(item, row[c], null);
            }
        }
    }
}