從一個 DataTable 篩選資料列(DataRow[]),嘗試加入另一個結構相同的 DataTable 時,直接呼叫 Rows.Add(DataRow) 會拋出例外:
「該行已經屬於另一個表」
DataTable dt = ds.Tables["All"].Clone(); // 僅複製結構
DataRow[] rows = dataSet.Tables["Product"].Select("bc=1");
foreach (DataRow dr in rows)
{
dt.Rows.Add(dr); // 拋出異常:行屬於另一個表
}
DataRow 物件擁有 Table 屬性,指向其所屬的 DataTable。一個 DataRow 實例同一時間只能屬於一個 DataTable。Rows.Add(DataRow) 會嘗試將該行「移動」到目標表,但來源表仍持有引用,導致衝突。
foreach (DataRow dr in rows)
{
dt.Rows.Add(dr.ItemArray); // 建立新行,複製值陣列
}
ItemArray 回傳 Object[],包含該行所有欄位值。Rows.Add(Object[]) 會在目標表建立全新的 DataRow,避免所有權衝突。
foreach (DataRow dr in rows)
{
dt.ImportRow(dr); // 內部自動處理值複製與狀態保留
}
差異:
Rows.Add(ItemArray):建立新行,RowState = AddedImportRow(DataRow):建立新行,保留原行的 RowState、錯誤資訊等中繼資料,適合需保持變更追蹤的場景DataTable source = GetSourceData();
DataTable target = source.Clone(); // 只要結構
// 篩選條件
DataRow[] filtered = source.Select("Status = 'Active'");
foreach (DataRow row in filtered)
{
target.ImportRow(row); // 推薦:保留狀態
}
// 或使用 LINQ (DataTableExtensions)
var query = source.AsEnumerable().Where(r => r.Field("Status") == "Active");
DataTable target2 = query.CopyToDataTable(); // 一行搞定,需引用 System.Data.DataSetExtensions
Clone() 只複製結構;Copy() 複製結構+資料DataRow[] 仍屬於原 DataTableDataTable 非執行緒安全,並行操作需加鎖或改用 ConcurrentQueue + 批次寫入遇到「該行已經屬於另一個表」,請記住核心原則:不要跨表共用 DataRow 實例。使用 ImportRow 或 Rows.Add(dr.ItemArray) 建立獨立副本,或直接用 CopyToDataTable()(LINQ)簡化代碼。