将 DataTable 转换为 IEnumerable<T>
Posted
技术标签:
【中文标题】将 DataTable 转换为 IEnumerable<T>【英文标题】:Convert DataTable to IEnumerable<T> 【发布时间】:2011-03-24 11:38:49 【问题描述】:我正在尝试将 DataTable 转换为 IEnumerable。其中 T 是我创建的自定义类型。我知道我可以通过创建List<T>
来做到这一点,但我在想是否有一种更巧妙的方法可以使用 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();
关键部分是我正在创建一个List<T>
,然后使用AsEnumerable()
返回它。
【问题讨论】:
【参考方案1】:该实现没有错。你可以试试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>
【讨论】:
【参考方案2】:还有一个名为“AsEnumerable()”的 DataSetExtension 方法(在 System.Data 中),它接受一个 DataTable 并返回一个 Enumerable。有关更多详细信息,请参阅the MSDN doc,但它基本上很简单:
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"]),
);
【讨论】:
哦,不错。我没有注意到dataTable.AsEnumerable()
,而且总是做得更长更丑:dataTable.Rows.Cast<DataSetName.SomeLongDataTableRowName>()
AsEnumerable
扩展方法位于 System.Data
命名空间中,但请务必引用 System.Data.DataSetExtensions
程序集以使用它。
对数据视图使用 .CastdataTable.Cast<DataRow>()
获取 IEnumerable 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;
【讨论】:
【参考方案4】:使用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));
或者,更好的是,创建一个TankReading(DataRow r)
构造函数,然后变成:
table.AsEnumerable().Select(row => return new TankReading(row));
【讨论】:
【参考方案5】:如果您从 SQL 查询中生成 DataTable
,您是否考虑过简单地使用 Dapper?
然后,而不是使用 SqlParameters
和 DataTable
和 DataAdapter
来制作 SqlCommand
等等,然后您必须费力地将其转换为类,您只需定义类,制作查询列名与字段名匹配,参数通过名称轻松绑定。您已经定义了 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/Plain Old Class Object):
// 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
);
尽管映射工作,这仍然没有数据表方法那么痛苦。这也可以让您执行某种逻辑,但如果逻辑不仅仅是非常简单的直接映射,我会将逻辑放入单独的 TankReadingMapper
类中。
【讨论】:
【参考方案6】:DataTable 的通用扩展方法。可能有人会很有趣。我从另一篇文章中获取创建动态属性的想法:https://***.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;
【讨论】:
【参考方案7】:如果您想将任何 DataTable 转换为等效的 IEnumerable 向量函数。
请看一下下面的通用函数,这可能对您的需求有所帮助(您可能需要根据您的需要包含针对不同数据类型的编写案例)。
/// <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();
【讨论】:
【参考方案8】:我在here 上写了一篇关于这个主题的文章。 我想它可以帮助你。
通常它会这样做:
static void Main(string[] args)
// Convert from a DataTable source to an IEnumerable.
var usersSourceDataTable = CreateMockUserDataTable();
var usersConvertedList = usersSourceDataTable.ToEnumerable<User>();
// Convert from an IEnumerable source to a DataTable.
var usersSourceList = CreateMockUserList();
var usersConvertedDataTable = usersSourceList.ToDataTable<User>();
【讨论】:
以上是关于将 DataTable 转换为 IEnumerable<T>的主要内容,如果未能解决你的问题,请参考以下文章
NPOI 将excel转换为datatable或者将datatable转换为excel
Cucumber DataTable 错误 - io.cucumber.datatable.UndefinedDataTableTypeException:无法将 DataTable 转换为 cucu