将 DataTable 转换为强类型对象列表

发布于 2025-01-03 13:23:37 字数 1123 浏览 1 评论 0原文

我正在尝试编写一个通用方法,将 DataTable 转换为强类型对象列表。

到目前为止我正在使用的代码是...

public List<T> ImportTable<T>(String fileName, String table)
{
    //Establish Connection to Access Database File
    var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");

    var tableData = new List<T>();

    foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
    {
        tableData.Add(ConvertRowToType<T>(row));
    }

    return tableData;
}

public T ConvertRowToType<T>(DataRow row)
{
    //??? What is the best thing to do here ???        
}

如果有人的建议需要对其进行更改,我不会专注于该代码。

假设我调用此函数并传入类型...

public class mdbConcern
{
    public Int32 ConcernId { get; set; }
    public String Concern { get; set; }
}

并且 DataTable 中返回的数据看起来像...

ConcernID  Concern
1          Law and Ethics
2          Mail
3          Business English
...        ...

实现 ConvertRowToType(DataRow row) 方法的最佳方法是什么?

有人可以告诉我如何使用 Func 作为参数之一,以便我可以传递一些映射信息吗?

I am trying to write a generic method that will convert a DataTable to a list of strongly typed objects.

The code that I'm working with so far is...

public List<T> ImportTable<T>(String fileName, String table)
{
    //Establish Connection to Access Database File
    var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");

    var tableData = new List<T>();

    foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
    {
        tableData.Add(ConvertRowToType<T>(row));
    }

    return tableData;
}

public T ConvertRowToType<T>(DataRow row)
{
    //??? What is the best thing to do here ???        
}

I'm not fixated on this code if anybody's suggestions would require changes to it.

So let's say I call this function passing in the type...

public class mdbConcern
{
    public Int32 ConcernId { get; set; }
    public String Concern { get; set; }
}

And the Data coming back in the DataTable looks like...

ConcernID  Concern
1          Law and Ethics
2          Mail
3          Business English
...        ...

What would be the best way to implement the ConvertRowToType(DataRow row) method?

Can someone show me how to use Func as one of the parameters so I can pass in some mapping information?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

独木成林 2025-01-10 13:23:37

我认为扩展方法是最好的方法:

public static class Helper
{
    public static T ToType<T>(this DataRow row) where T : new()
    {
        T obj = new T();
        var props = TypeDescriptor.GetProperties(obj);
        foreach (PropertyDescriptor prop in props)
        {
            if(row.Table.Columns.IndexOf(prop.Name) >= 0 
                && row[prop.Name].GetType() == prop.PropertyType)
            {   
                prop.SetValue(obj, row[prop.Name]);
            }
        }
        return obj;
    }
}

用法:

public List<T> ImportTable<T>(String fileName, String table)
{
    //Establish Connection to Access Database File
    var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");

    var tableData = new List<T>();

    foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
    {
        tableData.Add(row.ToType<T>());
    }

    return tableData;
}

更新我看到您要求一个Func来提供映射。我不确定您到底想做什么,但这是我想出的一种方法:

public class mdbConcern
{
    public Int32 ConcernId { get; set; }
    public String Concern { get; set; }

    public static PropertyDescriptor Mapping(string name)
    {
        PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(mdbConcern));
        switch (name)
        {
            case "Concern_Id":
                return props.GetByName("ConcernId");
            case "Concern":
                return props.GetByName("Concern");
            default:
                return null;
        }
    }
}

public static class Helper
{
    public static T ToType<T>(this DataRow row, Func<string, PropertyDescriptor> mapping) 
       where T : new()
    {
        T obj = new T();        
        foreach (DataColumn col in row.Table.Columns)
        {
            var prop = mapping(col.ColumnName);
            if(prop != null)
                prop.SetValue(obj, row[col]);
        }
        return obj;
    }
}

用法:

foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
    tableData.Add(row.ToType<mdbConcern>(mdbConcern.Mapping));
}

这是一个使用类型属性上的属性来存储其映射的版本。我认为这是一个更自然的解决方案:

[AttributeUsage(AttributeTargets.Property)]
public class ColumnMappingAttribute : Attribute
{
    public string Name { get; set; }
    public ColumnMappingAttribute(string name)
    {
        Name = name;
    }
}
public class mdbConcern
{
    ColumnMapping("Concern_Id")]
    public Int32 ConcernId { get; set; }
    ColumnMapping("Concern")]
    public String Concern { get; set; }
}

public static class Helper
{   
    public static T ToType<T>(this DataRow row) where T : new()
    {
        T obj = new T();
        var props = TypeDescriptor.GetProperties(obj);
        foreach (PropertyDescriptor prop in props)
        {
            var columnMapping = prop.Attributes.OfType<ColumnMappingAttribute>().FirstOrDefault();

            if(columnMapping != null)
            {
                if(row.Table.Columns.IndexOf(columnMapping.Name) >= 0 
                    && row[columnMapping.Name].GetType() == prop.PropertyType)
                {               
                    prop.SetValue(obj, row[columnMapping.Name]);
                }
            }
        }
        return obj;
    }
}

I think an extension method is the best way to go:

public static class Helper
{
    public static T ToType<T>(this DataRow row) where T : new()
    {
        T obj = new T();
        var props = TypeDescriptor.GetProperties(obj);
        foreach (PropertyDescriptor prop in props)
        {
            if(row.Table.Columns.IndexOf(prop.Name) >= 0 
                && row[prop.Name].GetType() == prop.PropertyType)
            {   
                prop.SetValue(obj, row[prop.Name]);
            }
        }
        return obj;
    }
}

Usage:

public List<T> ImportTable<T>(String fileName, String table)
{
    //Establish Connection to Access Database File
    var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");

    var tableData = new List<T>();

    foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
    {
        tableData.Add(row.ToType<T>());
    }

    return tableData;
}

Update I see that you asked for a Func that would provide the mapping. I'm not sure exactly what you envisioned but here is a method I came up with:

public class mdbConcern
{
    public Int32 ConcernId { get; set; }
    public String Concern { get; set; }

    public static PropertyDescriptor Mapping(string name)
    {
        PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(mdbConcern));
        switch (name)
        {
            case "Concern_Id":
                return props.GetByName("ConcernId");
            case "Concern":
                return props.GetByName("Concern");
            default:
                return null;
        }
    }
}

public static class Helper
{
    public static T ToType<T>(this DataRow row, Func<string, PropertyDescriptor> mapping) 
       where T : new()
    {
        T obj = new T();        
        foreach (DataColumn col in row.Table.Columns)
        {
            var prop = mapping(col.ColumnName);
            if(prop != null)
                prop.SetValue(obj, row[col]);
        }
        return obj;
    }
}

Usage:

foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
    tableData.Add(row.ToType<mdbConcern>(mdbConcern.Mapping));
}

Here's a version using attributes on the type's properties to store its mapping. I think it's a more natural solution:

[AttributeUsage(AttributeTargets.Property)]
public class ColumnMappingAttribute : Attribute
{
    public string Name { get; set; }
    public ColumnMappingAttribute(string name)
    {
        Name = name;
    }
}
public class mdbConcern
{
    ColumnMapping("Concern_Id")]
    public Int32 ConcernId { get; set; }
    ColumnMapping("Concern")]
    public String Concern { get; set; }
}

public static class Helper
{   
    public static T ToType<T>(this DataRow row) where T : new()
    {
        T obj = new T();
        var props = TypeDescriptor.GetProperties(obj);
        foreach (PropertyDescriptor prop in props)
        {
            var columnMapping = prop.Attributes.OfType<ColumnMappingAttribute>().FirstOrDefault();

            if(columnMapping != null)
            {
                if(row.Table.Columns.IndexOf(columnMapping.Name) >= 0 
                    && row[columnMapping.Name].GetType() == prop.PropertyType)
                {               
                    prop.SetValue(obj, row[columnMapping.Name]);
                }
            }
        }
        return obj;
    }
}
倚栏听风 2025-01-10 13:23:37

除了@Sorax 答案。我增强了 ToType 方法以支持 Nullable<> 类型成员(使用字段代替属性,使用 TypeInfo 代替 TypeDescriptor >)。它将整个DataTable对象作为参数并返回IList

    protected IList<TResult> TableToList<TResult>(DataTable table) where TResult : new()
    {
        var result = new List<TResult>(table.Rows.Count);

        var fields = typeof(TResult).GetTypeInfo().DeclaredFields;

        TResult obj;
        Object colVal;
        var columns = table.Columns;
        var nullableTypeDefinition = typeof(Nullable<>);
        var dbNullType = typeof(DBNull);
        Type[] genericArguments;

        foreach (DataRow row in table.Rows)
        {
            obj = new TResult();

            foreach (var f in fields)
            {
                if (columns.Contains(f.Name))
                {
                    colVal = row[f.Name];
                    if (colVal.GetType() == f.FieldType)
                    {
                        f.SetValue(obj, colVal);
                    }
                    else if (colVal.GetType() != dbNullType && f.FieldType.IsGenericType && 
                             f.FieldType.GetGenericTypeDefinition() == nullableTypeDefinition)
                    {
                            genericArguments = f.FieldType.GetGenericArguments();

                            if (genericArguments.Length > 0 && genericArguments[0] == colVal.GetType())
                            {
                                f.SetValue(obj, colVal);
                            }
                    }
                }
            }

            result.Add(obj);
        }

        return result;
    }

Addition to @Sorax answer. I enhanced ToType method to support Nullable<> type members (using fields instead of properties and TypeInfo instead of TypeDescriptor). It takes whole DataTable object as a paramater and returns IList.

    protected IList<TResult> TableToList<TResult>(DataTable table) where TResult : new()
    {
        var result = new List<TResult>(table.Rows.Count);

        var fields = typeof(TResult).GetTypeInfo().DeclaredFields;

        TResult obj;
        Object colVal;
        var columns = table.Columns;
        var nullableTypeDefinition = typeof(Nullable<>);
        var dbNullType = typeof(DBNull);
        Type[] genericArguments;

        foreach (DataRow row in table.Rows)
        {
            obj = new TResult();

            foreach (var f in fields)
            {
                if (columns.Contains(f.Name))
                {
                    colVal = row[f.Name];
                    if (colVal.GetType() == f.FieldType)
                    {
                        f.SetValue(obj, colVal);
                    }
                    else if (colVal.GetType() != dbNullType && f.FieldType.IsGenericType && 
                             f.FieldType.GetGenericTypeDefinition() == nullableTypeDefinition)
                    {
                            genericArguments = f.FieldType.GetGenericArguments();

                            if (genericArguments.Length > 0 && genericArguments[0] == colVal.GetType())
                            {
                                f.SetValue(obj, colVal);
                            }
                    }
                }
            }

            result.Add(obj);
        }

        return result;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文