将数据读取器中的行转换为类型化结果

发布于 2024-07-29 19:03:18 字数 293 浏览 4 评论 0原文

我正在使用返回数据读取器的第三方库。 我想要一种简单且尽可能通用的方法将其转换为对象列表。
例如,假设我有一个类“Employee”,它有 2 个属性 EmployeeId 和 Name,我希望将数据读取器(包含员工列表)转换为 List< 员工>.
我想我别无选择,只能迭代数据读取器的行,并将它们中的每一行转换为我将添加到列表中的 Employee 对象。 有更好的解决办法吗? 我使用的是 C# 3.5,理想情况下我希望它尽可能通用,以便它可以与任何类一起使用(DataReader 中的字段名称与各个对象的属性名称相匹配)。

I'm using a third party library which returns a data reader. I would like a simple way and as generic as possible to convert it into a List of objects.
For example, say I have a class 'Employee' with 2 properties EmployeeId and Name, I would like the data reader (which contains a list of employees) to be converted into List< Employee>.
I guess I have no choice but to iterate though the rows of the data reader and for each of them convert them into an Employee object that I will add to the List. Any better solution? I'm using C# 3.5 and ideally I would like it to be as generic as possible so that it works with any classes (the field names in the DataReader match the property names of the various objects).

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

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

发布评论

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

评论(12

薄情伤 2024-08-05 19:03:18

您真的需要一个列表吗?或者 IEnumerable 就足够了吗?

我知道您希望它是通用的,但更常见的模式是在接受数据行(或 IDataRecord)的目标对象类型上有一个静态工厂方法。 那看起来像这样:

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }

    public static Employee Create(IDataRecord record)
    {
        return new Employee
        {
           Id = record["id"],
           Name = record["name"]
        };
    }
}

public IEnumerable<Employee> GetEmployees()
{
    using (var reader = YourLibraryFunction())
    {
       while (reader.Read())
       {
           yield return Employee.Create(reader);
       }
    }
}

然后,如果您确实需要一个列表而不是 IEnumerable,则可以对结果调用.ToList()。 我想您还可以使用泛型+委托来使该模式的代码更可重用。

更新:我今天再次看到这个,想编写通用代码:

public IEnumerable<T> GetData<T>(IDataReader reader, Func<IDataRecord, T> BuildObject)
{
    try
    {
        while (reader.Read())
        {
            yield return BuildObject(reader);
        }
    }
    finally
    {
         reader.Dispose();
    }
}

//call it like this:
var result = GetData(YourLibraryFunction(), Employee.Create);

Do you really need a list, or would IEnumerable be good enough?

I know you want it to be generic, but a much more common pattern is to have a static Factory method on the target object type that accepts a datarow (or IDataRecord). That would look something like this:

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }

    public static Employee Create(IDataRecord record)
    {
        return new Employee
        {
           Id = record["id"],
           Name = record["name"]
        };
    }
}

.

public IEnumerable<Employee> GetEmployees()
{
    using (var reader = YourLibraryFunction())
    {
       while (reader.Read())
       {
           yield return Employee.Create(reader);
       }
    }
}

Then if you really need a list rather than an IEnumerable you can call .ToList() on the results. I suppose you could also use generics + a delegate to make the code for this pattern more re-usable as well.

Update: I saw this again today and felt like writing the generic code:

public IEnumerable<T> GetData<T>(IDataReader reader, Func<IDataRecord, T> BuildObject)
{
    try
    {
        while (reader.Read())
        {
            yield return BuildObject(reader);
        }
    }
    finally
    {
         reader.Dispose();
    }
}

//call it like this:
var result = GetData(YourLibraryFunction(), Employee.Create);
眼角的笑意。 2024-08-05 19:03:18

您可以构建一个扩展方法,例如:

public static List<T> ReadList<T>(this IDataReader reader, 
                                  Func<IDataRecord, T> generator) {
     var list = new List<T>();
     while (reader.Read())
         list.Add(generator(reader));
     return list;
}

并使用它:

var employeeList = reader.ReadList(x => new Employee {
                                               Name = x.GetString(0),
                                               Age = x.GetInt32(1)
                                        });

Joel 的建议是一个很好的建议。 您可以选择返回 IEnumerable。 改造上面的代码很容易:

public static IEnumerable<T> GetEnumerator<T>(this IDataReader reader, 
                                              Func<IDataRecord, T> generator) {
     while (reader.Read())
         yield return generator(reader);
}

如果你想自动将列映射到属性,代码思路是一样的。 您只需将上述代码中的 generator 函数替换为一个查询 typeof(T) 并通过读取匹配列使用反射设置对象属性的函数即可。 然而,我个人更喜欢定义一个工厂方法(就像 Joel 的答案中提到的那样)并将其委托传递给这个函数:

 var list = dataReader.GetEnumerator(Employee.Create).ToList();

You could build an extension method like:

public static List<T> ReadList<T>(this IDataReader reader, 
                                  Func<IDataRecord, T> generator) {
     var list = new List<T>();
     while (reader.Read())
         list.Add(generator(reader));
     return list;
}

and use it like:

var employeeList = reader.ReadList(x => new Employee {
                                               Name = x.GetString(0),
                                               Age = x.GetInt32(1)
                                        });

Joel's suggestion is a good one. You can choose to return IEnumerable<T>. It's easy to transform the above code:

public static IEnumerable<T> GetEnumerator<T>(this IDataReader reader, 
                                              Func<IDataRecord, T> generator) {
     while (reader.Read())
         yield return generator(reader);
}

If you want to automatically map the columns to properties, the code idea is the same. You can just replace the generator function in the above code with a function that interrogates typeof(T) and sets the properties on the object using reflection by reading the matched column. However, I personally prefer defining a factory method (like the one mentioned in Joel's answer) and passing a delegate of it into this function:

 var list = dataReader.GetEnumerator(Employee.Create).ToList();
夜声 2024-08-05 19:03:18

虽然我不建议在生产代码中这样做,但您可以使用反射和泛型自动执行此操作:

public static class DataRecordHelper
{
    public static void CreateRecord<T>(IDataRecord record, T myClass)
    {
        PropertyInfo[] propertyInfos = typeof(T).GetProperties();

        for (int i = 0; i < record.FieldCount; i++)
        {
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.Name == record.GetName(i))
                {
                    propertyInfo.SetValue(myClass, Convert.ChangeType(record.GetValue(i), record.GetFieldType(i)), null);
                    break;
                }
            }
        }
    }
}

public class Employee
{
    public int Id { get; set; }
    public string LastName { get; set; }
    public DateTime? BirthDate { get; set; }

    public static IDataReader GetEmployeesReader()
    {
        SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString);

        conn.Open();
        using (SqlCommand cmd = new SqlCommand("SELECT EmployeeID As Id, LastName, BirthDate FROM Employees"))
        {
            cmd.Connection = conn;
            return cmd.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }

    public static IEnumerable GetEmployees()
    {
        IDataReader rdr = GetEmployeesReader();
        while (rdr.Read())
        {
            Employee emp = new Employee();
            DataRecordHelper.CreateRecord<Employee>(rdr, emp);

            yield return emp;
        }
    }
}

然后您可以使用 CreateRecord() 从数据读取器中的字段实例化任何类。

<asp:GridView ID="GvEmps" runat="server" AutoGenerateColumns="true"></asp:GridView>

GvEmps.DataSource = Employee.GetEmployees();
GvEmps.DataBind();

Whilst I wouldn't recommend this for production code, but you can do this automatically using reflection and generics:

public static class DataRecordHelper
{
    public static void CreateRecord<T>(IDataRecord record, T myClass)
    {
        PropertyInfo[] propertyInfos = typeof(T).GetProperties();

        for (int i = 0; i < record.FieldCount; i++)
        {
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.Name == record.GetName(i))
                {
                    propertyInfo.SetValue(myClass, Convert.ChangeType(record.GetValue(i), record.GetFieldType(i)), null);
                    break;
                }
            }
        }
    }
}

public class Employee
{
    public int Id { get; set; }
    public string LastName { get; set; }
    public DateTime? BirthDate { get; set; }

    public static IDataReader GetEmployeesReader()
    {
        SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString);

        conn.Open();
        using (SqlCommand cmd = new SqlCommand("SELECT EmployeeID As Id, LastName, BirthDate FROM Employees"))
        {
            cmd.Connection = conn;
            return cmd.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }

    public static IEnumerable GetEmployees()
    {
        IDataReader rdr = GetEmployeesReader();
        while (rdr.Read())
        {
            Employee emp = new Employee();
            DataRecordHelper.CreateRecord<Employee>(rdr, emp);

            yield return emp;
        }
    }
}

You can then use CreateRecord<T>() to instantiate any class from the fields in a data reader.

<asp:GridView ID="GvEmps" runat="server" AutoGenerateColumns="true"></asp:GridView>

GvEmps.DataSource = Employee.GetEmployees();
GvEmps.DataBind();
老旧海报 2024-08-05 19:03:18

我们实施了以下解决方案,感觉效果很好。 它非常简单,并且比映射器需要更多的接线。 然而,有时手动控制是很好的,老实说,只要接线一次就可以了。

简而言之:我们的域模型实现了一个接口,该接口有一个方法,该方法接受 IDataReader 并从中填充模型属性。 然后,我们使用泛型和反射创建模型的实例并对其调用 Parse 方法。

我们考虑使用构造函数并将 IDataReader 传递给它,但我们所做的基本性能检查似乎表明该界面始终更快(即使只是快一点)。 此外,接口路由通过编译错误提供即时反馈。

我喜欢的一件事是,您可以使用 private set 来获取以下示例中的 Age 等属性,并直接从数据库中设置它们。

public interface IDataReaderParser
{
    void Parse(IDataReader reader);
}

public class Foo : IDataReaderParser
{
    public string Name { get; set; }
    public int Age { get; private set; }

    public void Parse(IDataReader reader)
    {
        Name = reader["Name"] as string;
        Age = Convert.ToInt32(reader["Age"]);
    }
}

public class DataLoader
{
    public static IEnumerable<TEntity> GetRecords<TEntity>(string connectionStringName, string storedProcedureName, IEnumerable<SqlParameter> parameters = null)
                where TEntity : IDataReaderParser, new()
    {
        using (var sqlCommand = new SqlCommand(storedProcedureName, Connections.GetSqlConnection(connectionStringName)))
        {
            using (sqlCommand.Connection)
            {
                sqlCommand.CommandType = CommandType.StoredProcedure;
                AssignParameters(parameters, sqlCommand);
                sqlCommand.Connection.Open();

                using (var sqlDataReader = sqlCommand.ExecuteReader())
                {
                    while (sqlDataReader.Read())
                    {
                        //Create an instance and parse the reader to set the properties
                        var entity = new TEntity();
                        entity.Parse(sqlDataReader);
                        yield return entity;
                    }
                }
            }
        }
    }
}

要调用它,您只需提供类型参数

IEnumerable<Foo> foos = DataLoader.GetRecords<Foo>(/* params */)

We have implemented the following solution and feel it works pretty well. It's pretty simple and requires a bit more wiring up then what a mapper would do. However, sometimes it is nice to have the manual control and honestly, you wire up once and you're done.

In a nutshell: Our domain models implement an interface that has a method that takes in an IDataReader and populates the model properties from it. We then use Generics and Reflection to create an instance of the model and call the Parse method on it.

We considered using a constructor and passing IDataReader to it, but the basic performance checks we did seemed to suggest the interface was consistently faster (if only by a little). Also, the interface route provides instant feedback via compilation errors.

One of the things I like, is that you can utilize private set for properties like Age in the example below and set them straight from the database.

public interface IDataReaderParser
{
    void Parse(IDataReader reader);
}

public class Foo : IDataReaderParser
{
    public string Name { get; set; }
    public int Age { get; private set; }

    public void Parse(IDataReader reader)
    {
        Name = reader["Name"] as string;
        Age = Convert.ToInt32(reader["Age"]);
    }
}

public class DataLoader
{
    public static IEnumerable<TEntity> GetRecords<TEntity>(string connectionStringName, string storedProcedureName, IEnumerable<SqlParameter> parameters = null)
                where TEntity : IDataReaderParser, new()
    {
        using (var sqlCommand = new SqlCommand(storedProcedureName, Connections.GetSqlConnection(connectionStringName)))
        {
            using (sqlCommand.Connection)
            {
                sqlCommand.CommandType = CommandType.StoredProcedure;
                AssignParameters(parameters, sqlCommand);
                sqlCommand.Connection.Open();

                using (var sqlDataReader = sqlCommand.ExecuteReader())
                {
                    while (sqlDataReader.Read())
                    {
                        //Create an instance and parse the reader to set the properties
                        var entity = new TEntity();
                        entity.Parse(sqlDataReader);
                        yield return entity;
                    }
                }
            }
        }
    }
}

To call it, you simply provide the type parameter

IEnumerable<Foo> foos = DataLoader.GetRecords<Foo>(/* params */)
撩起发的微风 2024-08-05 19:03:18

注意:这是 .NET Core 代码

一个愚蠢的性能选项,如果您不介意外部依赖项(令人惊叹的 Fast Member nuget 包):

public static T ConvertToObject<T>(this SqlDataReader rd) where T : class, new()
{

    Type type = typeof(T);
    var accessor = TypeAccessor.Create(type);
    var members = accessor.GetMembers();
    var t = new T();

    for (int i = 0; i < rd.FieldCount; i++)
    {
        if (!rd.IsDBNull(i))
        {
            string fieldName = rd.GetName(i);

            if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
            {
                accessor[t, fieldName] = rd.GetValue(i);
            }
        }
    }

    return t;
}

使用:

public IEnumerable<T> GetResults<T>(SqlDataReader dr) where T : class, new()
{
    while (dr.Read())
    {
        yield return dr.ConvertToObject<T>());
    }
}

NOTE: This is .NET Core code

A stupidly performant option, should you not mind an external dependency (the amazing Fast Member nuget package):

public static T ConvertToObject<T>(this SqlDataReader rd) where T : class, new()
{

    Type type = typeof(T);
    var accessor = TypeAccessor.Create(type);
    var members = accessor.GetMembers();
    var t = new T();

    for (int i = 0; i < rd.FieldCount; i++)
    {
        if (!rd.IsDBNull(i))
        {
            string fieldName = rd.GetName(i);

            if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
            {
                accessor[t, fieldName] = rd.GetValue(i);
            }
        }
    }

    return t;
}

To use:

public IEnumerable<T> GetResults<T>(SqlDataReader dr) where T : class, new()
{
    while (dr.Read())
    {
        yield return dr.ConvertToObject<T>());
    }
}
萤火眠眠 2024-08-05 19:03:18

像 Magic

我个人讨厌在构造函数中进行手动映射,我也不喜欢自己进行反射。 因此,这里有另一个由精彩的(而且相当普遍的)Newtonsoft JSON 库提供的解决方案。

仅当您的属性名称与数据读取器列名称完全匹配时它才有效,但它对我们来说效果很好。

...假设您有一个数据读取器名称“yourDataReader”...

        var dt = new DataTable();
        dt.Load(yourDataReader);
        // creates a json array of objects
        string json = Newtonsoft.Json.JsonConvert.SerializeObject(dt);
        // this is what you're looking for right??
        List<YourEntityType> list = 
Newtonsoft.Json.JsonConvert
.DeserializeObject<List<YourEntityType>>(json);

Like Magic

I personally HATE doing manual mapping in constructors, I'm also not a fan of doing my own reflection. So here's another solution courtesy of the wonderful (and fairly ubiquitous) Newtonsoft JSON lib.

It will only work if your property names exactly match the datareader column names, but it worked well for us.

...assumes you've got a datareader name "yourDataReader"...

        var dt = new DataTable();
        dt.Load(yourDataReader);
        // creates a json array of objects
        string json = Newtonsoft.Json.JsonConvert.SerializeObject(dt);
        // this is what you're looking for right??
        List<YourEntityType> list = 
Newtonsoft.Json.JsonConvert
.DeserializeObject<List<YourEntityType>>(json);
生生漫 2024-08-05 19:03:18

最新版本的 C#/.NET5 提供了一个名为“源生成器”的新功能,我强烈建议每个人都去探索一下。

简而言之,它允许您在编译时生成 C# 代码,该代码将为您进行手动映射。 它显然比任何类型的反射(即使使用缓存和类似的速度黑客)快 20-30 倍 - 因为它实际上只是将属性分配给 IDataReader 字段。

我在这里没有可以分享的简短代码片段,因为源生成器仍然有点复杂(它应该驻留在单独的程序集中,并且您必须学习 Roslyn API 等),但我确实相信这是一个值得每个人探索的很棒的功能ORM 世界及周边。

我开始尝试源生成器并喜欢它: https://github.com/jitbit/MapDataReader 感觉可以自由地从我的存储库中“窃取”一些代码。

免责声明:我分享了我自己的 github 存储库的链接

Latest versions of C#/.NET5 offer a new awesome feature called "source generators", that I urge everyone to explore.

In a nutshell it allows you to generate C# code at compilation time, the code that will do manual mapping for you. It's obviously 20-30 times faster than any kind of reflection (even with caching and similar speed hacks) - because it literally just assigns properties to IDataReader fields.

There's no short code snippet I could share here, since source generators are still a bit complicated (it should reside in a separate assembly and you will have to learn Roslyn API's etc.) but I do believe it's an awesome feature worth exploring for everyone in the ORM world and around.

I started experimenting with source generators and loving it: https://github.com/jitbit/MapDataReader feel free to "steal" some code from my repo.

disclaimer: I shared a link to my own github repo

任性一次 2024-08-05 19:03:18

最简单的解决方案:

var dt=new DataTable();
dt.Load(myDataReader);
list<DataRow> dr=dt.AsEnumerable().ToList();

然后选择它们以便将它们映射到任何类型。

The simplest Solution :

var dt=new DataTable();
dt.Load(myDataReader);
list<DataRow> dr=dt.AsEnumerable().ToList();

Then select them in order to map them to any type.

南街女流氓 2024-08-05 19:03:18

对于.NET Core 2.0:

这是一个与.NET CORE 2.0配合使用的扩展方法,可以执行RAW SQL并将结果映射到任意类型的LIST:

用法:

 var theViewModel = new List();
 string theQuery = @"SELECT * FROM dbo.Something";
 theViewModel = DataSQLHelper.ExecSQL(theQuery,_context);

 using Microsoft.EntityFrameworkCore;
 using System.Data;
 using System.Data.SqlClient;
 using System.Reflection;

public static List ExecSQL(string query, myDBcontext context)
 {
 using (context)
 {
 using (var command = context.Database.GetDbConnection().CreateCommand())
 {
 command.CommandText = query;
 command.CommandType = CommandType.Text;
 context.Database.OpenConnection();
                using (var result = command.ExecuteReader())
                {
                    List<T> list = new List<T>();
                    T obj = default(T);
                    while (result.Read())
                    {
                        obj = Activator.CreateInstance<T>();
                        foreach (PropertyInfo prop in obj.GetType().GetProperties())
                        {
                            if (!object.Equals(result[prop.Name], DBNull.Value))
                            {
                                prop.SetValue(obj, result[prop.Name], null);
                            }
                        }
                        list.Add(obj);
                    }
                    return list;

                }
            }
        }
    }

For .NET Core 2.0:

Here is an extension method that works with .NET CORE 2.0 to execute RAW SQL and map results to LIST of arbitrary types:

USAGE:

 var theViewModel = new List();
 string theQuery = @"SELECT * FROM dbo.Something";
 theViewModel = DataSQLHelper.ExecSQL(theQuery,_context);

 using Microsoft.EntityFrameworkCore;
 using System.Data;
 using System.Data.SqlClient;
 using System.Reflection;

public static List ExecSQL(string query, myDBcontext context)
 {
 using (context)
 {
 using (var command = context.Database.GetDbConnection().CreateCommand())
 {
 command.CommandText = query;
 command.CommandType = CommandType.Text;
 context.Database.OpenConnection();
                using (var result = command.ExecuteReader())
                {
                    List<T> list = new List<T>();
                    T obj = default(T);
                    while (result.Read())
                    {
                        obj = Activator.CreateInstance<T>();
                        foreach (PropertyInfo prop in obj.GetType().GetProperties())
                        {
                            if (!object.Equals(result[prop.Name], DBNull.Value))
                            {
                                prop.SetValue(obj, result[prop.Name], null);
                            }
                        }
                        list.Add(obj);
                    }
                    return list;

                }
            }
        }
    }
风吹短裙飘 2024-08-05 19:03:18

我的版本

用法:

var Q = await Reader.GetTable<DbRoom>("SELECT id, name FROM rooms");

PgRoom is

public class DbRoom
{
    [Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public string Name { get; set; }
}

Reader.GetTable 包含:

            using (var R = await Q.ExecuteReaderAsync())
            {
                List<T> Result = new List<T>();

                Dictionary<int, PropertyInfo> Props = new Dictionary<int, PropertyInfo>();

                foreach (var p in typeof(T).GetProperties())
                {
                    for (int i = 0; i < R.FieldCount; i++)
                    {
                        if (p.GetCustomAttributes<ColumnAttribute>().FirstOrDefault(t => t.Name == R.GetName(i)) != null
                            && p.PropertyType == R.GetFieldType(i))
                        {
                            Props.Add(i, p);
                        }
                    }
                }

                while (await R.ReadAsync())
                {
                    T row = new T();

                    foreach (var kvp in Props)
                    {
                        kvp.Value.SetValue(row, R[kvp.Key]);
                    }

                    Result.Add(row);
                }

                return Result;
            }

My version

Usage:

var Q = await Reader.GetTable<DbRoom>("SELECT id, name FROM rooms");

PgRoom is

public class DbRoom
{
    [Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public string Name { get; set; }
}

Reader.GetTable contains:

            using (var R = await Q.ExecuteReaderAsync())
            {
                List<T> Result = new List<T>();

                Dictionary<int, PropertyInfo> Props = new Dictionary<int, PropertyInfo>();

                foreach (var p in typeof(T).GetProperties())
                {
                    for (int i = 0; i < R.FieldCount; i++)
                    {
                        if (p.GetCustomAttributes<ColumnAttribute>().FirstOrDefault(t => t.Name == R.GetName(i)) != null
                            && p.PropertyType == R.GetFieldType(i))
                        {
                            Props.Add(i, p);
                        }
                    }
                }

                while (await R.ReadAsync())
                {
                    T row = new T();

                    foreach (var kvp in Props)
                    {
                        kvp.Value.SetValue(row, R[kvp.Key]);
                    }

                    Result.Add(row);
                }

                return Result;
            }
风透绣罗衣 2024-08-05 19:03:18

我找到了这个解决方案。

var cmd = ctx.Connection.CreateCommand();

T result = DbDataReaderdHelper.Fill<T>(cmd)

public static class DbDataReaderdHelper
{
    public static List<T> Fill<T>(DbCommand dbCommand) where T : new()
    {
        List<T> result = new List<T>();
        var reader = dbCommand.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Type type = typeof(T);
                T obj = (T)Activator.CreateInstance(type);
                PropertyInfo[] properties = type.GetProperties();

                foreach (PropertyInfo property in properties)
                {
                    var value = reader[property.Name];

                    try
                    {
                        if (value != null)
                        {
                            var convertedValue = TypeDescriptor.GetConverter(property.PropertyType).ConvertFromInvariantString(value.ToString());

                            property.SetValue(obj, convertedValue);
                        }
                    }
                    catch {}
                }
                result.Add(obj);
            }
        }

        reader.Close();

        return result;
    }
}

I found this solution.

var cmd = ctx.Connection.CreateCommand();

T result = DbDataReaderdHelper.Fill<T>(cmd)

public static class DbDataReaderdHelper
{
    public static List<T> Fill<T>(DbCommand dbCommand) where T : new()
    {
        List<T> result = new List<T>();
        var reader = dbCommand.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Type type = typeof(T);
                T obj = (T)Activator.CreateInstance(type);
                PropertyInfo[] properties = type.GetProperties();

                foreach (PropertyInfo property in properties)
                {
                    var value = reader[property.Name];

                    try
                    {
                        if (value != null)
                        {
                            var convertedValue = TypeDescriptor.GetConverter(property.PropertyType).ConvertFromInvariantString(value.ToString());

                            property.SetValue(obj, convertedValue);
                        }
                    }
                    catch {}
                }
                result.Add(obj);
            }
        }

        reader.Close();

        return result;
    }
}
一片旧的回忆 2024-08-05 19:03:18

请检查此答案

使用反射 + lambda 表达式进行简单、快速的 IDataReaderIEnumerable 转换

https://stackoverflow.com/a/70321210/3343007

Please check this answer

Simple, Fast IDataReader to IEnumerable<T> conversion using reflection + lambda expressions

https://stackoverflow.com/a/70321210/3343007

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