在C#中将DataTable转换为通用列表

nav*_*een 26 c# linq generics datatable lambda

免责声明:我知道它在SO的很多地方都被问过.
我的查询有点不同.

编码语言:C#3.5

我有一个名为cardsTable的DataTable从DB中提取数据,我有一个类卡,它只有一些属性(没有构造函数)

public class Cards
{
    public Int64 CardID { get; set; }
    public string CardName { get; set; }
    public Int64 ProjectID { get; set; }
    public Double CardWidth { get; set; }
    public Double CardHeight { get; set; }
    public string Orientation { get; set; }
    public string BackgroundImage { get; set; }
    public string Background { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我想将cardsTable数据插入List类型的对象.
我的数据将在其中包含空字段,因此当我转换数据时该方法不应该出错.以下方法是最好的方法吗?

DataTable dt = GetDataFromDB();
List<Cards> target = dt.AsEnumerable().ToList().ConvertAll(x => new Cards { CardID = (Int64)x.ItemArray[0] });
Run Code Online (Sandbox Code Playgroud)

Jef*_*ado 34

你实际上可以大大缩短它.您可以将Select()扩展方法视为类型转换器.然后可以将转换写为:

List<Cards> target = dt.AsEnumerable()
    .Select(row => new Cards
    {
        // assuming column 0's type is Nullable<long>
        CardID = row.Field<long?>(0).GetValueOrDefault(),
        CardName = String.IsNullOrEmpty(row.Field<string>(1))
            ? "not found"
            : row.Field<string>(1),
    }).ToList();
Run Code Online (Sandbox Code Playgroud)


Tom*_*son 12

我认为如果你使用一些约定和反射,所有的解决方案都可以改进并使方法更通用.比方说,你的名字在数据表名称相同的列在你的对象的属性,那么你可以写一下你的对象的所有属性的东西,然后在数据表映射值查找该列.

我做了相反的事情,那就是......从IList到datatable,我写的代码可以在以下网址看到:http://blog.tomasjansson.com/convert-datatable-to-generic-list-extension/

从另一个角度来看应该不难,并且应该很难重载功能,以便您可以提供要包含或排除哪些属性的信息.

编辑: 所以使其工作的代码是:

public static class DataTableExtensions
{
    private static Dictionary<Type,IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();
    public static IList<PropertyInfo> GetPropertiesForType<T>()
    {
        var type = typeof(T);
        if(!typeDictionary.ContainsKey(typeof(T)))
        {
            typeDictionary.Add(type, type.GetProperties().ToList());
        }
        return typeDictionary[type];
    }

    public static IList<T> ToList<T>(this DataTable table) where T : new()
    {
        IList<PropertyInfo> properties = GetPropertiesForType<T>();
        IList<T> result = new List<T>();

        foreach (var row in table.Rows)
        {
            var item = CreateItemFromRow<T>((DataRow)row, properties);
            result.Add(item);
        }

        return result;
    }

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
    {
        T item = new T();
        foreach (var property in properties)
        {
            property.SetValue(item, row[property.Name], null);
        }
        return item;
    }

}
Run Code Online (Sandbox Code Playgroud)

如果你有一个DataTable,你可以写yourTable.ToList<YourType>(),它会为你创建列表.如果您有更复杂的类型与嵌套对象,您需要更新代码.一个建议是重载ToList方法以接受params string[] excludeProperties包含不应映射的所有属性的方法.当然,您可以foreachCreateItemForRow方法的循环中添加一些空检查.

更新:添加静态字典以存储反射操作的结果,使其更快一点.我没有编译代码,但它应该工作:).


Fre*_*kyB 8

只是一点点简化.我不使用ItemArray:

List<Person> list = tbl.AsEnumerable().Select(x => new Person
                    {
                        Id = (Int32) (x["Id"]),
                        Name = (string) (x["Name"] ?? ""),
                        LastName = (string) (x["LastName"] ?? "")
                    }).ToList();
Run Code Online (Sandbox Code Playgroud)


Jam*_*iec 6

.ToList()位置错误,如果某些字段可以为null,则必须处理这些字段,因为如果它们为null,它们将不会转换为Int64

DataTable dt = GetDataFromDB();
List<Cards> target = dt.AsEnumerable().Select(
  x => new Cards { CardID = (Int64)(x.ItemArray[0] ?? 0) }).ToList();
Run Code Online (Sandbox Code Playgroud)