資料庫欄位儲存 JSON 字串:[{"name":"張三","time":"8.5"},{"name":"李四","time":"9.1"}]。新增資料時,若 name 相同則以新資料取代舊資料,最終回寫合併後的 JSON。
public void InsertOrUpdateOnlyItem(List<ImportEntity> newList)
{
var toInsert = new List<ImportEntity>();
var toUpdate = new List<ImportEntity>();
foreach (var entity in newList)
{
// 1. 查詢是否已存在 (依複合鍵)
var existing = Repository.Find(e =>
e.TaskId == entity.TaskId &&
e.BizCatId == entity.BizCatId &&
e.ItemCode == entity.ItemCode &&
e.Number == entity.Number &&
e.SampleCode == entity.SampleCode);
if (existing != null)
{
// 2. 反序列化雙方 JSON
var serializer = new JavaScriptSerializer();
var oldData = serializer.Deserialize<List<Dictionary<string, string>>>(existing.ImportData);
var newData = serializer.Deserialize<List<Dictionary<string, string>>>(entity.ImportData);
// 3. Union 去重:自定義比較器只比較 name
var comparer = new NameEqualityComparer();
var merged = newData.Union(oldData, comparer).ToList(); // 新資料在前,同 name 會保留新資料
// 4. 序列化回寫
existing.ImportData = serializer.Serialize(merged);
toUpdate.Add(existing);
}
else
{
toInsert.Add(entity);
}
}
Repository.UpdateAll(toUpdate);
Repository.InsertAll(toInsert);
Repository.Save();
}
public class NameEqualityComparer : IEqualityComparer<Dictionary<string, string>>
{
public bool Equals(Dictionary<string, string> x, Dictionary<string, string> y)
{
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
// 核心邏輯:name 相同即視為相同
return x.TryGetValue("name", out var nx) &&
y.TryGetValue("name", out var ny) &&
nx == ny;
}
public int GetHashCode(Dictionary<string, string> obj)
{
if (obj == null) return 0;
return obj.TryGetValue("name", out var name) ? name.GetHashCode() : 0;
}
}
舊資料: [ {name:"張三", time:"8.5"}, {name:"李四", time:"9.1"} ]
新資料: [ {name:"張三", time:"12"}, {name:"王五", time:"4"} ]
↓ Union(new, old, comparer) (新資料在前)
結果: [ {name:"張三", time:"12"}, {name:"王五", time:"4"}, {name:"李四", time:"9.1"} ]
↑ 張三保留新資料(time=12) ↑ 王五為新增 ↑ 李四僅存在於舊資料保留
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| LINQ Union + 比較器 | 語意清晰、一行完成去重、強型別 | 需反序列化/序列化、大量資料效能一般 | 中小筆資料、邏輯複雜 |
| ToDictionary(name, item) | O(N) 查找、效能最佳、程式碼簡短 | 需手動合併邏輯 | 高效能需求、單一鍵值去重 |
| JSON.NET JArray Merge | 不需定義強型別、動態操作 | 動態型別編譯期無檢查 | 結構不固定、快速原型 |
var dict = oldData.ToDictionary(d => d["name"]); // 以 name 為鍵
foreach (var item in newData)
{
dict[item["name"]] = item; // 新資料直接覆蓋
}
var merged = dict.Values.ToList();
Queryable.Union` / `Enumerable.Union` 搭配自定義 `IEqualityComparer` 是表達「依特定欄位去重」最語意化的 LINQ 寫法。若資料量大 (超過萬筆),建議改用 `ToDictionary` 或資料庫層面的 `MERGE` / `UPSERT` 邏輯,避免在應用層搬運大量 JSON 字串。