首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

LINQ to SQL 進階系列(三):CUD 操作與日誌記錄

     編輯:關於.NET

CUD 代表 Create(新增)、Update(更新)、Delete(刪除)三種基本資料操作。雖然這些主題已被廣泛討論,但 LINQ to SQL 仍有一些獨特的實作細節值得深入探討。

1. 動態建立資料庫

LINQ to SQL 提供 CreateDatabase() 方法,讓開發者能夠透過物件關聯設計工具(OR Designer)定義實體類別與欄位對應,再直接建立資料庫結構。這完美支援「先有領域模型,後有資料庫結構」的自上而下開發模式。

Northwind db = new Northwind("YourConnectionString"); // 資料庫名稱設為不存在的名稱
db.Log = Console.Out;
if (!db.DatabaseExists())
    db.CreateDatabase(); // 自動建立資料庫與資料表

雖然 OR Designer 視覺化操作方便,但在複雜資料庫屬性設定上較為繁瑣,實務上常採用「先設計資料表,再反向產生實體類別」的工作流程。

2. 新增資料

2.0 基本插入操作

建立實體物件,加入對應的 Table<T> 集合,呼叫 SubmitChanges() 即可完成插入:

var newCustomer = new Customer {
    CustomerID = "MCSFT",
    CompanyName = "Microsoft",
    ContactName = "John Doe",
    ContactTitle = "Sales Manager",
    Address = "1 Microsoft Way",
    City = "Redmond",
    Region = "WA",
    PostalCode = "98052",
    Country = "USA",
    Phone = "(425) 555-1234",
    Fax = null
};
db.Customers.Add(newCustomer);
db.SubmitChanges();

2.1 處理資料庫自動產生的欄位(Identity/AutoSync)

以 Orders 表的 OrderID 為例,其對應屬性標記為:

[Column(Storage="_OrderID", AutoSync=AutoSync.OnInsert, 
         DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int OrderID { get; set; }

AutoSync=AutoSync.OnInsert 指示執行期在插入後自動取回資料庫產生的值(如 SCOPE_IDENTITY())並回填至物件。實測程式碼:

Orders o = new Orders();
o.ShipAddress = "Test";
db.Orders.Add(o);
db.SubmitChanges();
Console.WriteLine(o.OrderID); // 此處已可取得資料庫產生的 OrderID

產生的 SQL 顯示 LINQ to SQL 自動包裝交易,先執行 INSERT,緊接著查詢 SCOPE_IDENTITY() 確保取回正確值:

INSERT INTO [dbo].[Orders]([CustomerID], [EmployeeID], ...) VALUES (@p0, @p1, ...)
SELECT [t0].[OrderID] FROM [dbo].[Orders] AS [t0] WHERE [t0].[OrderID] = (SCOPE_IDENTITY())

2.2 一對多關聯的級聯新增

當新增「一」端實體(如 Category)時,若其導覽屬性集合(Products)已關聯新實體,呼叫 SubmitChanges() 會自動級聯插入「多」端資料:

var newCategory = new Category { CategoryName = "Widgets", Description = "..." };
var newProduct = new Product { ProductName = "Blue Widget", UnitPrice = 34.56M, Category = newCategory };
db.Categories.Add(newCategory);
db.SubmitChanges(); // 同時插入 Category 與 Product

2.3 多對多關聯的手動維護

多對多關聯(透過中介表)需手動建立三端實體並分別加入對應集合:

var newEmployee = new Employee { FirstName = "Kira", LastName = "Smith" };
var newTerritory = new Territory { TerritoryID = "12345", TerritoryDescription = "Anytown", Region = db.Regions.First() };
var newEmployeeTerritory = new EmployeeTerritory { Employee = newEmployee, Territory = newTerritory };

db.Employees.Add(newEmployee);
db.Territories.Add(newTerritory);
db.EmployeeTerritories.Add(newEmployeeTerritory);
db.SubmitChanges();

3. 更新資料與跨 DataContext 處理

同一 DataContext 內更新極為簡單:查詢物件、修改屬性、呼叫 SubmitChanges()。跨 DataContext 更新需使用 Attach() 方法,並建議在主鍵屬性加上 IsVersion=true 以利樂觀並發檢查:

[Column(Storage="_PageID", ..., IsVersion=true)]
public int PageID { get; set; }

關鍵原則:更新哪個實體,就 Attach 哪個實體。 錯誤範例:修改 Order.Customer.City 卻 Attach(Order),會導致錯誤插入新客戶;正確做法是 Attach(o.Customer, true)。

// 第一個 Context 取得資料
nwind.Order o = null;
using (nwind.Northwind db = new nwind.Northwind(constr)) {
    o = db.Orders.First();
    o.Customer.City = "new city";
}

// 第二個 Context 更新
using (nwind.Northwind db = new nwind.Northwind(constr)) {
    db.Log = Console.Out;
    // db.Orders.Attach(o, true); // 錯誤:會嘗試插入新 Customer
    db.Customers.Attach(o.Customer, true); // 正確
    db.SubmitChanges();
}

4. 刪除資料

使用 Remove() 方法。一對多關聯中,必須先刪除「多」端明細,再刪除「一」端主檔,否則會違反外鍵限制:

var order = (from o in db.Orders
             where o.CustomerID == "WARTH" && o.EmployeeID == 3
             select o).First();

foreach (OrderDetail od in order.OrderDetails)
    db.OrderDetails.Remove(od);

db.Orders.Remove(order);
db.SubmitChanges();

5. SQL 日誌記錄

將 DataContext.Log 設定為任何 TextWriter(如 Console.Out、StreamWriter)即可記錄所有產生的 SQL 指令,利於偵錯與效能分析:

StreamWriter sw = new StreamWriter("log.txt", true);
db.Log = sw;
var q = db.Customers.Select(c => c).ToList();
sw.Close();

6. 並發衝突與交易處理

LINQ to SQL 內建樂觀並發支援(透過 IsVersion 或 UpdateCheck),衝突時拋出 ChangeConflictException。交易方面,SubmitChanges() 預設即包裝於隱性交易中;若需明確控制,可使用 TransactionScope 或 db.Transaction 屬性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved