我有一个数据DataReader
,我希望将其转换为List
.对此有什么简单的解决方案?
例如,在CustomerEntity类中,我有CustomerId和CustomerName属性.如果我的DataReader将这两列作为数据返回,那么我该如何将其转换为List
.
我建议为此编写一个扩展方法:
public static IEnumerableSelect (this IDataReader reader, Func projection) { while (reader.Read()) { yield return projection(reader); } }
然后,您可以使用LINQ的ToList()
方法将其转换为a,List
如果您愿意,如下所示:
using (IDataReader reader = ...) { Listcustomers = reader.Select(r => new Customer { CustomerId = r["id"] is DBNull ? null : r["id"].ToString(), CustomerName = r["name"] is DBNull ? null : r["name"].ToString() }).ToList(); }
我实际上建议FromDataReader
在Customer
(或其他地方)放一个方法:
public static Customer FromDataReader(IDataReader reader) { ... }
这会留下:
using (IDataReader reader = ...) { Listcustomers = reader.Select (Customer.FromDataReader) .ToList(); }
(我不认为类型推断在这种情况下会起作用,但我可能是错的...)
我使用这种情况编写了以下方法.
首先,添加命名空间: System.Reflection
例如:T
是返回类型(ClassName),dr
是映射的参数DataReader
C#,调用映射方法如下:
ListpersonList = new List (); personList = DataReaderMapToList (dataReaderForPerson);
这是映射方法:
public static ListDataReaderMapToList (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
我见过在属性或字段上使用Reflection和属性将DataReaders映射到对象的系统.(像什么LinqToSql确实有点.)他们节省一点打字,并可能降低错误的数量编码的DBNull时等,一旦您缓存生成的代码,他们可以更快那么大多数手写代码为好,所以不要考虑了如果你这么做的话,那就是"高速公路".
有关此示例,请参阅".NET中的反射防御".
然后你可以写代码
class CustomerDTO { [Field("id")] public int? CustomerId; [Field("name")] public string CustomerName; }
...
using (DataReader reader = ...) { Listcustomers = reader.AutoMap () .ToList(); }
(AutoMap(),是一种扩展方法)
@Stilgar,感谢一个伟大的评论
如果能,你很可能会使用更好 的NHibernate,EF或LINQ到SQL等 但是在旧的项目(或其他(有时是有效的)的原因,如"非我发明"等"存储特效的爱")并不总是可以使用ORM,所以较轻的系统可以用来"穿上你的袖子"
如果您每个人都需要编写大量IDataReader循环,您将看到减少编码(和错误)的好处,而无需更改您正在使用的系统的体系结构.这并不是说它是一个很好的架构开始......
我假设CustomerDTO不会离开数据访问层,复合对象等将由数据访问层使用DTO对象构建.
最简单的解决方案:
var dt=new DataTable(); dt.Load(myDataReader); listdr=dt.AsEnumerable().ToList();
我会(并且已经)开始使用Dapper.使用你的例子就像(从内存中写):
public ListGetCustomerList() { using (DbConnection connection = CreateConnection()) { return connection.Query ("procToReturnCustomers", commandType: CommandType.StoredProcedure).ToList(); } }
CreateConnection()
将处理访问您的数据库并返回连接.
Dapper自动处理将数据字段映射到属性.它还支持多种类型和结果集,速度非常快.
IEnumerable
因此查询返回ToList()
.
您不能(直接)将datareader转换为列表.
您必须遍历datareader中的所有元素并插入到列表中
在示例代码下面
using (drOutput) { System.Collections.Generic.ListarrObjects = 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); } }
显而易见@Ian Ringrose
的是,你应该使用库的中心论点是这里最好的单一答案(因此是+1),但是对于最小的一次性或演示代码,这里是@SLaks
对@Jon Skeet
细粒度的微妙评论的具体例证(+ 1来)回答:
public ListLoad( < > ) { 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:另请参阅一些优秀的进一步细微编码技术,以便在此答案中拆分样板