将数据表转换为通用IEnumerable <T>

时间:2019-12-06 11:58:52

标签: c# generics .net-core datatable ado.net

我正在尝试从DataTable转换为IEnumberable<T>。但是我得到的是IEnumberable<DataRow>

using (var con = new SqlConnection(Connection.ToString()))
{
    con.Open();
    using (var cmd = con.CreateCommand())
    {
        cmd.CommandTimeout = int.MaxValue;
        cmd.CommandText = sqlCommand;
        var reader = cmd.ExecuteReader();
        DataTable tbl = new DataTable();
        tbl.Load(reader);
        var res = tbl.AsEnumerable().ToList();  // IEnumberable<DataRow>:

    }
}

我想做的是:

protected async Task<IEnumerable<T>> QuerySqlCmdReadRows<T>(string sqlCommand)
{
    using (var con = new SqlConnection(Connection.ToString()))
    {
        con.Open();
        using (var cmd = con.CreateCommand())
        {
            cmd.CommandTimeout = int.MaxValue;
            cmd.CommandText = sqlCommand;
            var reader = cmd.ExecuteReader();
            DataTable tbl = new DataTable();
            tbl.Load(reader);
            return tbl.AsEnumerable().ToList(); 

        }
    }

}

是否有可能像在Entity Framework中一样获得IEnumberable?

var studentList = ctx.SqlQuery("Select * from Students")
                    .ToList<Student>();

2 个答案:

答案 0 :(得分:2)

您可以创建一个扩展方法来为您转换它。假设查询中的属性与常规T类型的属性相匹配,则可以使用反射来执行它!有关示例(请参见代码中的注释):

public static class DataTableExtensions
{
    public static IEnumerable<T> ToGenericList<T>(this DataTable dataTable)
    {
        var properties = typeof(T).GetProperties().Where(x => x.CanWrite).ToList();

        var result = new List<T>();

        // loop on rows
        foreach (DataRow row in dataTable.Rows)
        {
            // create an instance of T generic type.
            var item = Activator.CreateInstance<T>();

            // loop on properties and columns that matches properties
            foreach (var prop in properties)
                foreach (DataColumn column in dataTable.Columns)                    
                    if (prop.Name == column.ColumnName)
                    {
                        // Get the value from the datatable cell
                        object value = row[column.ColumnName];

                        // Set the value into the object
                        prop.SetValue(item, value);
                        break;
                    }


            result.Add(item);
        }

        return result;
    }
}

假设您有一个这样的模型:

public class Student 
{
    public int Id { get; set; } 
    public string Code { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    // other possible properties
}

您可以使用如下扩展方法:

protected async Task<IEnumerable<T>> QuerySqlCmdReadRows<T>(string sqlCommand)
{
    using (var con = new SqlConnection(Connection.ToString()))
    {
        con.Open();
        using (var cmd = con.CreateCommand())
        {
            cmd.CommandText = sqlCommand;

            DataTable dtResult = new DataTable();

            using (var reader = await cmd.ExecuteReaderAsync())
               dtResult.Load(reader);

            return dtResult.ToGenericList<T>();
        }
    }
}

// just and sample
var students = QuerySqlCmdReadRows<Students>("select Id, Code, FirstName, LastName from Students");

答案 1 :(得分:2)

这是我将DataTable转换为IEnumerable<T>的实现。它会复制具有匹配属性或字段名称的任何列。

首先,有几个扩展用于将MemberInfo作为PropertyInfoFieldInfo的父级:

public class MemberInfoExt {
    public static bool GetCanWrite(this MemberInfo member) {
        switch (member) {
            case FieldInfo mfi:
                return true;
            case PropertyInfo mpi:
                return mpi.CanWrite;
            default:
                throw new ArgumentException("MemberInfo must be if type FieldInfo or PropertyInfo", nameof(member));
        }
    }
    public static void SetValue(this MemberInfo member, object destObject, object value) {
        switch (member) {
            case FieldInfo mfi:
                mfi.SetValue(destObject, value);
                break;
            case PropertyInfo mpi:
                mpi.SetValue(destObject, value);
                break;
            case MethodInfo mi:
                mi.Invoke(destObject, new object[] { value });
                break;
            default:
                throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo or MethodInfo", nameof(member));
        }
    }
}

使用这些,您可以编写一个DataTable扩展方法:

public class DataTableExt {
    public static IEnumerable<T> ToIEnumerable<T>(this DataTable dt) where T : new() {
        var props = typeof(T).GetPropertiesOrFields()
                             .Where(mi => dt.Columns.Contains(mi.Name) && mi.GetCanWrite())
                             .ToList();
        foreach (var row in dt.AsEnumerable()) {
            var aT = new T();
            foreach (var p in props)
                p.SetValue(aT, row[p.Name]);
            yield return aT;
        }
    }
}

并使用它将DataTable转换为特定类型。

也有可能将DataTable转换为IEnumerable<anon>,其中anon是一个匿名对象(或模拟),但这具有可疑的价值,因为它需要在以下位置创建一个匿名对象运行时,只能通过反射使用。