You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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值和类型兼容问题。

最终得到的结果表完全符合你的期望:

timecol1col2col3aa_col1
2018-10-05 00:00:002414157
2018-10-06 00:00:00443586
2018-10-06 00:00:0063786
2018-10-07 00:00:0014516

内容的提问来源于stack exchange,提问作者Redballs Ishchenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:38:08