針對含外鍵關聯的實體(如 Publisher 1:N Book),LINQ to SQL 提供直觀的集合操作介面自動維護關聯欄位。
Publisher.Books 為 EntitySet<Book>,建構子註冊關聯維護委托:
this._Books = new EntitySet<Book>(
new Action<Book>(attach_Books),
new Action<Book>(detach_Books));
private void attach_Books(Book entity) { entity.Publisher = this; }
private void detach_Books(Book entity) { entity.Publisher = null; }
Book.Publisher 設定時同步更新 PublisherID 外鍵值,實現雙向同步。
var publisher = new Publisher { Name = "Microsoft" };
publisher.Books.Add(new Book { Title = "Expert F#" });
publisher.Books.Add(new Book { Title = "Beautiful Code" });
context.Publishers.InsertOnSubmit(publisher);
context.SubmitChanges(); // 自動處理 PublisherID 回寫與 Book 外鍵填充
var publisher = context.Publishers.Single(p => p.PublisherID == guid);
publisher.Name = "Updated";
var book = publisher.Books.First();
book.Title = "Updated Book";
context.SubmitChanges(); // 追蹤到兩個物件皆異動,各自產生 UPDATE
注意:LINQ to SQL 無所謂「級聯更新」,僅是變更追蹤機制偵測到多個物件異動並各自提交。
publisher.Books.Remove(book); // 呼叫 detach_Books → Book.PublisherID = Guid.Empty
context.SubmitChanges(); // 拋出異常:外鍵不可為 null
解決:在 Book.PublisherID 的 ColumnAttribute 設定 DeleteOnNull=true,Context 偵測外鍵為預設值時改執行 DELETE 而非 UPDATE。
LINQ to SQL不支援級聯刪除,需手動處理或仰賴資料庫外鍵 Cascade Delete:
context.Books.DeleteAllOnSubmit(publisher.Books);
context.Publishers.DeleteOnSubmit(publisher);
context.SubmitChanges(); // 隱式交易保證原子性
預設延遲載入:存取 publisher.Books 時才查詢。可用 DataLoadOptions 指定急切載入:
var option = new DataLoadOptions();
option.LoadWith<Publisher>(p => p.Books);
context.LoadOptions = option; // 單一 JOIN 查詢取得主從資料
單一 DataContext/單執行緒下運作正常,跨上下文或併發場景需注意物件追蹤與關聯載入時機,後續文章將深入探討。