如何用LINQ连接两个C# DataTable,免枚举全列并转为目标DataTable?
解决LINQ连接DataTable后无法转换为DataTable的问题
你碰到的这个问题其实很常见——CopyToDataTable()方法只认**IEnumerable<DataRow>类型的集合**,而你当前LINQ查询返回的是包含DataRow和单独字段的匿名类型,完全不符合它的泛型约束,所以编译器直接报错了。
下面给你两种可行的解决方案,都能完美实现「保留tt所有动态列 + 添加aa的col1(重命名为aa_col1)」的需求:
方案一:手动构建结果表(轻量直接)
这种方法不需要额外写扩展方法,先定义好结果表的结构,再遍历LINQ连接的结果逐行填充数据,逻辑清晰易懂:
// 1. 复制tt的所有列结构,再添加需要的aa_col1列 DataTable resultTable = tt.Clone(); // 只复制结构,不包含数据 resultTable.Columns.Add("aa_col1", typeof(int)); // 2. 执行你原来的LINQ连接查询 var joinedData = from t in tt.AsEnumerable() join a in aa.AsEnumerable() on t.Field<DateTime>("time") equals a.Field<DateTime>("time") select new { TRow = t, ACol1 = a.Field<int>("col1") }; // 3. 遍历查询结果,把数据填充到新表中 foreach (var item in joinedData) { DataRow newRow = resultTable.NewRow(); // 复制tt当前行的所有列数据(动态适配所有列) foreach (DataColumn col in tt.Columns) { newRow[col.ColumnName] = item.TRow[col]; } // 添加aa表的col1数据 newRow["aa_col1"] = item.ACol1; resultTable.Rows.Add(newRow); }
方案二:自定义扩展方法(通用复用)
如果之后你还需要处理类似「匿名类型转DataTable」的场景,可以写一个通用的扩展方法,一次性解决这类问题:
public static class DataTableExtensions { public static DataTable CopyToDataTable<T>(this IEnumerable<T> source) { if (source == null) throw new ArgumentNullException(nameof(source)); DataTable resultTable = new DataTable(); PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); // 先根据匿名类型的属性创建结果表的列 foreach (PropertyInfo prop in properties) { // 如果属性是DataRow类型,就遍历它的所有列添加到结果表 if (prop.PropertyType == typeof(DataRow)) { var sampleRow = prop.GetValue(source.First()) as DataRow; foreach (DataColumn col in sampleRow.Table.Columns) { resultTable.Columns.Add(col.ColumnName, col.DataType); } } else { // 普通属性直接作为列添加 resultTable.Columns.Add(prop.Name, prop.PropertyType); } } // 填充数据到结果表 foreach (var item in source) { DataRow newRow = resultTable.NewRow(); foreach (PropertyInfo prop in properties) { if (prop.PropertyType == typeof(DataRow)) { var row = prop.GetValue(item) as DataRow; foreach (DataColumn col in row.Table.Columns) { newRow[col.ColumnName] = row[col] ?? DBNull.Value; } } else { newRow[prop.Name] = prop.GetValue(item) ?? DBNull.Value; } } resultTable.Rows.Add(newRow); } return resultTable; } }
有了这个扩展方法,你就可以直接用你原来的LINQ查询调用CopyToDataTable()了:
var result = from t in tt.AsEnumerable() join a in aa.AsEnumerable() on t.Field<DateTime>("time") equals a.Field<DateTime>("time") select new { t, aa_col1 = a.Field<int>("col1") }; DataTable resultTable = result.CopyToDataTable();
两种方案对比
- 方案一更轻量,适合单次使用,不需要额外维护扩展方法;
- 方案二更通用,适合需要多次处理类似场景的情况,但要注意处理
null值和类型兼容问题。
最终得到的结果表完全符合你的期望:
| time | col1 | col2 | col3 | aa_col1 |
|---|---|---|---|---|
| 2018-10-05 00:00:00 | 24 | 14 | 15 | 7 |
| 2018-10-06 00:00:00 | 4 | 43 | 58 | 6 |
| 2018-10-06 00:00:00 | 6 | 3 | 78 | 6 |
| 2018-10-07 00:00:00 | 1 | 4 | 5 | 16 |
内容的提问来源于stack exchange,提问作者Redballs Ishchenko
相关产品推荐
相关产品推荐

