将DataTable转换为IEnumerable <T>

我想将一个DataTable转换为IEnumerable。 其中T是我创build的自定义types。 我知道我可以通过创build一个列表来做到这一点,但我认为有一种使用IEnumerable的方法。 这是我现在拥有的。

private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable) { var tankReadings = new List<TankReading>(); foreach (DataRow row in dataTable.Rows) { var tankReading = new TankReading { TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), TankID = Convert.ToInt32(row["TankID"]), ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), ReadingInches = Convert.ToInt32(row["ReadingInches"]), MaterialNumber = row["MaterialNumber"].ToString(), EnteredBy = row["EnteredBy"].ToString(), ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), MaterialID = Convert.ToInt32(row["MaterialID"]), Submitted = Convert.ToBoolean(row["Submitted"]), }; tankReadings.Add(tankReading); } return tankReadings.AsEnumerable(); } 

关键部分是我创build一个List,然后使用AsEnumerable()返回它。

这个实现没有错。 你可以给yield关键字一个镜头,看看你喜欢它:

 private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable) { foreach (DataRow row in dataTable.Rows) { yield return new TankReading { TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), TankID = Convert.ToInt32(row["TankID"]), ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), ReadingInches = Convert.ToInt32(row["ReadingInches"]), MaterialNumber = row["MaterialNumber"].ToString(), EnteredBy = row["EnteredBy"].ToString(), ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), MaterialID = Convert.ToInt32(row["MaterialID"]), Submitted = Convert.ToBoolean(row["Submitted"]), }; } } 

另外, AsEnumerable不是必须的,因为List<T>已经是一个IEnumerable<T>

还有一个叫做“AsEnumerable()”的DataSetExtension方法(在System.Data中),它接受一个DataTable并返回一个Enumerable。 有关更多详细信息,请参阅MSDN文档 ,但基本上如下所示:

 dataTable.AsEnumerable() 

缺点是它枚举DataRow,而不是你的自定义类。 “Select()”LINQ调用可以转换行数据,但是:

 private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable) { return dataTable.AsEnumerable().Select(row => new TankReading { TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), TankID = Convert.ToInt32(row["TankID"]), ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), ReadingInches = Convert.ToInt32(row["ReadingInches"]), MaterialNumber = row["MaterialNumber"].ToString(), EnteredBy = row["EnteredBy"].ToString(), ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), MaterialID = Convert.ToInt32(row["MaterialID"]), Submitted = Convert.ToBoolean(row["Submitted"]), }); } 

使用System.Data.DataSetExtensions简单方法:

 table.AsEnumerable().Select(row => new TankReading{ TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), TankID = Convert.ToInt32(row["TankID"]), ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), ReadingInches = Convert.ToInt32(row["ReadingInches"]), MaterialNumber = row["MaterialNumber"].ToString(), EnteredBy = row["EnteredBy"].ToString(), ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), MaterialID = Convert.ToInt32(row["MaterialID"]), Submitted = Convert.ToBoolean(row["Submitted"]), }); 

要么:

 TankReading TankReadingFromDataRow(DataRow row){ return new TankReading{ TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), TankID = Convert.ToInt32(row["TankID"]), ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), ReadingInches = Convert.ToInt32(row["ReadingInches"]), MaterialNumber = row["MaterialNumber"].ToString(), EnteredBy = row["EnteredBy"].ToString(), ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), MaterialID = Convert.ToInt32(row["MaterialID"]), Submitted = Convert.ToBoolean(row["Submitted"]), }; } // Now you can do this table.AsEnumerable().Select(row => return TankReadingFromDataRow(row)); 

或者,更好的是,创build一个TankReading(DataRow r)构造函数,然后这变成:

  table.AsEnumerable().Select(row => return new TankReading(row)); 

如果你从SQL查询生成DataTable ,你有没有考虑简单地使用Dapper呢?

然后,而不是使用SqlParametersDataTableDataAdapter来创buildSqlCommand ,然后您必须费力地将其转换为类,您只需定义该类,使查询列名与字段名称匹配,并且参数很容易被名字绑定。 你已经定义了TankReading类,所以它会非常简单!

 using Dapper; // Below can be SqlConnection cast to DatabaseConnection, too. DatabaseConnection connection = // whatever IEnumerable<TankReading> tankReadings = connection.Query<TankReading>( "SELECT * from TankReading WHERE Value = @value", new { value = "tank1" } // note how `value` maps to `@value` ); return tankReadings; 

现在不是那么棒? Dapper是非常优化的,并会给你提供与DataAdapter直接读取相当的性能。

如果你的类有任何逻辑,或者是不变的或者没有无参数的构造函数,那么你可能需要有一个DbTankReading类(作为一个纯POCO /普通旧类对象):

 // internal because it should only be used in the data source project and not elsewhere internal sealed class DbTankReading { int TankReadingsID { get; set; } DateTime? ReadingDateTime { get; set; } int ReadingFeet { get; set; } int ReadingInches { get; set; } string MaterialNumber { get; set; } string EnteredBy { get; set; } decimal ReadingPounds { get; set; } int MaterialID { get; set; } bool Submitted { get; set; } } 

你会这样使用:

 IEnumerable<TankReading> tankReadings = connection .Query<DbTankReading>( "SELECT * from TankReading WHERE Value = @value", new { value = "tank1" } // note how `value` maps to `@value` ) .Select(tr => new TankReading( tr.TankReadingsID, tr.ReadingDateTime, tr.ReadingFeet, tr.ReadingInches, tr.MaterialNumber, tr.EnteredBy, tr.ReadingPounds, tr.MaterialID, tr.Submitted }); 

尽pipe进行了映射工作,但仍然比数据表方法痛苦不less。 这也可以让你执行某种逻辑,但是如果逻辑不是非常简单的直接映射,我会把逻辑放到一个单独的TankReadingMapper类中。

DataTable的通用扩展方法。 可能会有人变得有趣。 创builddynamic属性的想法我从另一篇文章: https : //stackoverflow.com/a/15819760/8105226

  public static IEnumerable<dynamic> AsEnumerable(this DataTable dt) { List<dynamic> result = new List<dynamic>(); Dictionary<string, object> d; foreach (DataRow dr in dt.Rows) { d = new Dictionary<string, object>(); foreach (DataColumn dc in dt.Columns) d.Add(dc.ColumnName, dr[dc]); result.Add(GetDynamicObject(d)); } return result.AsEnumerable<dynamic>(); } public static dynamic GetDynamicObject(Dictionary<string, object> properties) { return new MyDynObject(properties); } public sealed class MyDynObject : DynamicObject { private readonly Dictionary<string, object> _properties; public MyDynObject(Dictionary<string, object> properties) { _properties = properties; } public override IEnumerable<string> GetDynamicMemberNames() { return _properties.Keys; } public override bool TryGetMember(GetMemberBinder binder, out object result) { if (_properties.ContainsKey(binder.Name)) { result = _properties[binder.Name]; return true; } else { result = null; return false; } } public override bool TrySetMember(SetMemberBinder binder, object value) { if (_properties.ContainsKey(binder.Name)) { _properties[binder.Name] = value; return true; } else { return false; } } } 

如果要将任何DataTable转换为等效的IEnumerablevector函数。

请看下面的通用函数,这可能会帮助您的需求(您可能需要根据您的需要包含针对不同数据types的写入案例)。

 /// <summary> /// Get entities from DataTable /// </summary> /// <typeparam name="T">Type of entity</typeparam> /// <param name="dt">DataTable</param> /// <returns></returns> public IEnumerable<T> GetEntities<T>(DataTable dt) { if (dt == null) { return null; } List<T> returnValue = new List<T>(); List<string> typeProperties = new List<string>(); T typeInstance = Activator.CreateInstance<T>(); foreach (DataColumn column in dt.Columns) { var prop = typeInstance.GetType().GetProperty(column.ColumnName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); if (prop != null) { typeProperties.Add(column.ColumnName); } } foreach (DataRow row in dt.Rows) { T entity = Activator.CreateInstance<T>(); foreach (var propertyName in typeProperties) { if (row[propertyName] != DBNull.Value) { string str = row[propertyName].GetType().FullName; if (entity.GetType().GetProperty(propertyName).PropertyType == typeof(System.String)) { object Val = row[propertyName].ToString(); entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, Val, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); } else if (entity.GetType().GetProperty(propertyName).PropertyType == typeof(System.Guid)) { object Val = Guid.Parse(row[propertyName].ToString()); entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, Val, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); } else { entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, row[propertyName], BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); } } else { entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, null, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); } } returnValue.Add(entity); } return returnValue.AsEnumerable(); } 
  PagedDataSource objPage = new PagedDataSource(); DataView dataView = listData.DefaultView; objPage.AllowPaging = true; objPage.DataSource = dataView; objPage.PageSize = PageSize; TotalPages = objPage.PageCount; objPage.CurrentPageIndex = CurrentPage - 1; //Convert PagedDataSource to DataTable System.Collections.IEnumerator pagedData = objPage.GetEnumerator(); DataTable filteredData = new DataTable(); bool flagToCopyDTStruct = false; while (pagedData.MoveNext()) { DataRowView rowView = (DataRowView)pagedData.Current; if (!flagToCopyDTStruct) { filteredData = rowView.Row.Table.Clone(); flagToCopyDTStruct = true; } filteredData.LoadDataRow(rowView.Row.ItemArray, true); } //Here is your filtered DataTable return filterData;