当前位置:  开发笔记 > 编程语言 > 正文

如何轻松将DataReader转换为List <T>?

如何解决《如何轻松将DataReader转换为List<T>?》经验,为你挑选了7个好方法。

我有一个数据DataReader,我希望将其转换为List.对此有什么简单的解决方案?

例如,在CustomerEntity类中,我有CustomerId和CustomerName属性.如果我的DataReader将这两列作为数据返回,那么我该如何将其转换为List.



1> Jon Skeet..:

我建议为此编写一个扩展方法:

public static IEnumerable Select(this IDataReader reader,
                                       Func projection)
{
    while (reader.Read())
    {
        yield return projection(reader);
    }
}

然后,您可以使用LINQ的ToList()方法将其转换为a,List如果您愿意,如下所示:

using (IDataReader reader = ...)
{
    List customers = reader.Select(r => new Customer {
        CustomerId = r["id"] is DBNull ? null : r["id"].ToString(),
        CustomerName = r["name"] is DBNull ? null : r["name"].ToString() 
    }).ToList();
}

我实际上建议FromDataReaderCustomer(或其他地方)放一个方法:

public static Customer FromDataReader(IDataReader reader) { ... }

这会留下:

using (IDataReader reader = ...)
{
    List customers = reader.Select(Customer.FromDataReader)
                                     .ToList();
}

(我不认为类型推断在这种情况下会起作用,但我可能是错的...)


请注意,这与`reader.Cast ()相同.选择`.
扩展方法不应该是:while(reader.Read())而不是while(drOutput.Read())

2> 小智..:

我使用这种情况编写了以下方法.

首先,添加命名空间: System.Reflection

例如:T是返回类型(ClassName),dr是映射的参数DataReader

C#,调用映射方法如下:

List personList = new List();
personList = DataReaderMapToList(dataReaderForPerson);

这是映射方法:

public static List DataReaderMapToList(IDataReader dr)
{
    List list = new List();
    T obj = default(T);
    while (dr.Read()) {
        obj = Activator.CreateInstance();
        foreach (PropertyInfo prop in obj.GetType().GetProperties()) {
            if (!object.Equals(dr[prop.Name], DBNull.Value)) {
                prop.SetValue(obj, dr[prop.Name], null);
            }
        }
        list.Add(obj);
    }
    return list;
}

VB.NET,调用映射方法如下:

Dim personList As New List(Of Person)
personList = DataReaderMapToList(Of Person)(dataReaderForPerson)

这是映射方法:

Public Shared Function DataReaderMapToList(Of T)(ByVal dr As IDataReader) As List(Of T)
        Dim list As New List(Of T)
        Dim obj As T
        While dr.Read()
            obj = Activator.CreateInstance(Of T)()
            For Each prop As PropertyInfo In obj.GetType().GetProperties()
                If Not Object.Equals(dr(prop.Name), DBNull.Value) Then
                    prop.SetValue(obj, dr(prop.Name), Nothing)
                End If
            Next
            list.Add(obj)
        End While
        Return list
    End Function


这很好用.我对C#代码有一个小建议:将以`prop.SetValue`开头的行更改为`prop.SetValue(obj,Convert.ChangeType(dr [prop.Name],prop.PropertyType),null);`.这将使代码适用于字符串以外的类型.

3> Ian Ringrose..:

我见过在属性或字段上使用Reflection和属性将DataReaders映射到对象的系统.(像什么LinqToSql确实有点.)他们节省一点打字,并可能降低错误的数量编码的DBNull时等,一旦您缓存生成的代码,他们可以更快那么大多数手写代码为好,所以不要考虑了如果你这么做的话,那就是"高速公路".

有关此示例,请参阅".NET中的反射防御".

然后你可以写代码

class CustomerDTO  
{
    [Field("id")]
    public int? CustomerId;

    [Field("name")]
    public string CustomerName;
}

...

using (DataReader reader = ...)
{    
   List customers = reader.AutoMap()
                                    .ToList();
}

(AutoMap(),是一种扩展方法)


@Stilgar,感谢一个伟大的评论

如果,你很可能会使用更好 的NHibernate,EF或LINQ到SQL等 但是在旧的项目(或其他(有时是有效的)的原因,如"非我发明"等"存储特效的爱")并不总是可以使用ORM,所以较轻的系统可以用来"穿上你的袖子"

如果您每个人都需要编写大量IDataReader循环,您将看到减少编码(和错误)的好处,而无需更改您正在使用的系统的体系结构.这并不是说它是一个很好的架构开始......

我假设CustomerDTO不会离开数据访问层,复合对象等将由数据访问层使用DTO对象构建.


这种方法的问题在于,一旦开始使用复合对象,就会遇到很多麻烦.如果客户与公司关联,则您需要公司财产.您可以进行递归,但公司可能具有List 属性,然后您必须遍历图形.为此,您需要一个表示关联的属性.这就是LINQ to SQL和Entity Framework所做的,但它们是大型产品,您无法轻松开发内部解决方案.如果你打算这样做,为什么不使用EF呢?
我需要添加什么参考来使`[Field("id")]`工作?
@Stilgar使用EF对现有数据库使用"非EF规则"是一个非常令人信服的理由*不*使用EF ..(不,这个评论不会迟到6年,因为EF仍然有与之前相同的问题然后)
为什么我没有AutoMap()
你在某处实现了AutoMap吗?

4> Mohsen..:

最简单的解决方案:

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



5> Phil Cooper..:

我会(并且已经)开始使用Dapper.使用你的例子就像(从内存中写):

public List GetCustomerList()
{
    using (DbConnection connection = CreateConnection())
    {
        return connection.Query("procToReturnCustomers", commandType: CommandType.StoredProcedure).ToList();
    }
}

CreateConnection() 将处理访问您的数据库并返回连接.

Dapper自动处理将数据字段映射到属性.它还支持多种类型和结果集,速度非常快.

IEnumerable因此查询返回ToList().



6> RameshVel..:

您不能(直接)将datareader转换为列表.

您必须遍历datareader中的所有元素并插入到列表中

在示例代码下面

using (drOutput)   
{
            System.Collections.Generic.List arrObjects = new System.Collections.Generic.List();        
            int customerId = drOutput.GetOrdinal("customerId ");
            int CustomerName = drOutput.GetOrdinal("CustomerName ");

        while (drOutput.Read())        
        {
            CustomerEntity obj=new CustomerEntity ();
            obj.customerId = (drOutput[customerId ] != Convert.DBNull) ? drOutput[customerId ].ToString() : null;
            obj.CustomerName = (drOutput[CustomerName ] != Convert.DBNull) ? drOutput[CustomerName ].ToString() : null;
            arrObjects .Add(obj);
        }

}



7> Ruben Bartel..:

显而易见@Ian Ringrose的是,你应该使用库的中心论点是这里最好的单一答案(因此是+1),但是对于最小的一次性或演示代码,这里是@SLaks@Jon Skeet细粒度的微妙评论的具体例证(+ 1来)回答:

public List Load( <> )
{
    using ( var connection = CreateConnection() )
    using ( var command = Create<>Command( <>, connection ) )
    {
        connection.Open();
        using ( var reader = command.ExecuteReader() )
            return reader.Cast()
                .Select( x => new XXX( x.GetString( 0 ), x.GetString( 1 ) ) )
                .ToList();
    }
}

正如在@Jon Skeet答案中所说的那样

            .Select( x => new XXX( x.GetString( 0 ), x.GetString( 1 ) ) )

bit可以被提取到一个帮助器中(我喜欢将它们转储到查询类中):

    public static XXX FromDataRecord( this IDataRecord record)
    {
        return new XXX( record.GetString( 0 ), record.GetString( 1 ) );
    }

用作:

            .Select( FromDataRecord )

更新3月9日13:另请参阅一些优秀的进一步细微编码技术,以便在此答案中拆分样板

推荐阅读
Chloemw
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有