如何将DataRow转换为对象

15

我在我的项目中创建了一个DataRow:

DataRow datarow;

我想将这个DataRow转换为任何类型的对象,应该怎么做?


DataRow默认是一个对象。你想要实现什么目标? - IDeveloper
@IDeveloper 我想将数据行转换为给定类型的对象,并带有值。 - folk
1
Entity Framework 怎么样? - OneFineDay
@DonA 我对Entity Framework一无所知。 - folk
“Entity Framework” 将数据库转换成一组具有属性的强类型类 - 很棒的东西!Google一下吧! - OneFineDay
11个回答

29

这是我使用的一种相当酷的方式。

    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);
    }

2
整洁清晰的方法 - Nitin Sawant
2
当属性的类型是可空类型时,Convert.ChangeType会抛出异常。此外,如果尝试设置一个不可设置的属性,则可能会引发另一个异常。 - user8249395
1
我已经重构了代码以支持所有场景,请进行测试。 - Jonathan Santiago
1
如果 (type.IsEnum) 的话,可能需要添加枚举处理 { value = Enum.Parse(type, value.ToString()); } - Paul

15

我已经找到了一个适用于我的应用程序的解决方案。

    // 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);
            }
        }
    }

这将把你的 DataRow 映射到 ViewModel,如下所示。
Your_ViewModel model = CreateItemFromRow<Your_ViewModel>(row);

2
虽然我的数据模型中的整数类型(即Int32 vs Int64)之间存在冲突,但这对我起作用了,即使我认为不应该出现这种情况。将它们全部更改为Int64就解决了问题,但我希望找到原因。 - codah
1
我仍在使用它。只是我正在进行从C++到C#的大规模升级,所以我正在从各个地方获取片段,并且很少有时间深入研究这些问题。作为一个C#新手,非常感谢你的代码! - codah
1
@pdschuller,你的viewModel将包含与表中相同的属性和列名,它是区分大小写的。因此,您必须确保您想要从表中映射的任何属性都应该在ViewModel中存在。 - Bharat
2
啊啊啊,它就在你的原始帖子中。 Your_ViewModel model = CreateItemFromRow<Your_ViewModel>(row); - pdschuller
1
太棒了!谢谢你这个。 - EE_Kraig
显示剩余5条评论

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"] ;

或者

Person person = new Person();
person.FirstName = dataRow.Field<string>("FirstName");
person.LastName = dataRow.Field<string>("LastName");

3
这并不被称为转换!这只是赋值操作! - gyousefi

6

与之前的一些方法类似,我为DataRow创建了这个扩展方法,它接受一个要填充的参数对象。主要的区别在于除了填充对象的属性之外,它还填充了给定对象的字段。这应该也适用于更简单的结构(尽管我只在对象上进行了测试)。

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;
}

您可以将此代码放在当前类或全局静态类中。 它需要以下命名空间...
using System;
using System.Data;
using System.Reflection;

使用方法非常简单...

MyClassName obj = dataRow.ToObject<MyClassName>()

1
太棒了!像魔法一样运行,正是所需之物。 - Zujaj Misbah Khan

5

这里有一个扩展方法,可以将 DataRow 转换为指定的对象。

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;
    }
}

你可以像这样使用上述扩展方法。
foreach (DataRow row in dataTable.Rows)
{
    SomeClassType obj = row.Cast<SomeClassType>();
    // do something with your object
}

2

如果给定的 Converter<TIn, TOut> 是一个委托,那么以下内容应该可以工作:

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


1
除了 Avi 展示的手动方法,您还可以使用像 AutoMapper 这样的映射系统来为您执行转换。这在您需要映射很多列/属性的情况下特别有用。
请查看 此文章 以了解如何使用 AutoMapper 将 DataTable 转换为对象列表。

N.B. 当使用DataReaderMapper时,如果源类型和目标类型不同,则自动映射程序无法处理。 - Jack Ukleja
@Schneider,由于问题明确是关于将DataRow转换为另一种对象类型,因此不存在问题。 - Corey
但DataRow是“无类型”的。Automapper将无法推断要映射到什么,因此您又回到了起点。然后,OP可以调查AutoMappers IDataReader映射功能,这确实可以进行聪明的操作,将DataRows转换为强类型,这就是我最初评论的内容。 - Jack Ukleja

0
您可以像下面的代码一样将整个数据表转换为列表对象。当然,您也可以使用索引或字段值来获取您想要的特定对象。
    /// <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;
    }

0
DataRow有一个名为ItemArray的属性,其中包含一个对象值数组。您可以使用此数组并创建任何自定义类型,其中包含来自DataRow的值。

0

只需两个简单的步骤即可完成任务: 1. 转换为字典(ToDictionary)。 2. 将字典映射到实体(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;
    }

...而使用方法如下:

DataRow dr = getSomeDataRow(someArgs);
ABC result = dr.ToDictionary()
  .MapToEntity<ABC>();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接