翻譯整理ScottGu的關於LINQ to SQL的Part 4: Updating our Database 。該Post講解了如何使用LINQ to SQL更新資料庫,以及如何整合商務邏輯和自訂驗證邏輯。開發環境為.NET Framework 3.5 Beta2,開發工具為Visual Studio 2008 Beta2。
使用LINQ to SQL建模Northwind資料庫
在這之前一起學過LINQ to SQL設計器的使用,下面就使用如下的資料模型:
當使用LINQ to SQL設計器設計以上定義的五個類(Product,Category,Customer,Order和OrderDetail)的時候,每個類中的屬性都映射了相應資料庫中表的列,每個類的執行個體則代表了資料庫表中的一條記錄。另外,當定義資料模型時,LINQ to SQL設計器同樣會建立一個自訂DataContext類,來作為資料庫查詢和應用程式更新/變化的主要渠道。以上資料模型中定義的DataContext類命名為“NorthwindDataContext”。該類中包含了代表每個建模資料庫表的屬性。
使用LINQ文法運算式可以十分簡單的使用NorthwindDataContext類來查詢和檢索資料庫中的資料。LINQ to SQL會在運行時自動的轉換LINQ運算式到適當的SQL代碼來執行。例如,編寫以下LINQ運算式來根據Product Name檢索單個Product對象:
還可以使用LINQ運算式來檢索所有不存在於Order Details中的,並且UnitPrice大於100的所以Product:
變化跟蹤和DataContext.SubmitChanges()
當執行查詢和檢索像Product執行個體這樣的對象時,LINQ to SQL會自動保持對這些對象任何變化或更新的跟蹤。我們可以進行任意次數的查詢,以及使用LINQ to SQL的DataContext類作出更新,而這些變化都會被全部跟蹤。
注意:LINQ to SQL的變化跟蹤發生於調用者端——而不是在資料庫中。這就意味著使用跟蹤不會銷耗任何資料庫資源,也不需要在資料庫中改變/安裝任何組件模組。
當對從LINQ to SQL中檢索的對象作出更改之後,我們可以選擇調用DataContext上的SubmitChange()方法來應用變化返回到資料庫。這將會導致LINQ to SQL動態計算並執行適當的SQL代碼來更新資料庫。例如,編寫以下代碼更新資料庫中Product Name為“Chai”的Product上的UnitPrice和UnitsInStock:
當在以上代碼中調用northwind.SubmitChanges()方法時,LINQ to SQL會動態構建並執行一個更新這兩個Product屬性值的SQL“UPDATE”代碼模組。
在下面代碼中我們來遍曆不流行的,昂貴的Product,並把它們的ReorderLevel屬性設為0:
當在以上代碼中調用northwind.SubmitChanges()方法時,LINQ to SQL會計算並執行一組適當的UPDATE代碼模組來修改RecorderLevel屬性已變化的Product。
注意,如果一個Product的屬性沒有通過屬性指定而發生變化,則該對象不會被認為是發生變化的,並且LINQ to SQL也不會對於該對象執行更新回資料庫的操作。例如,如果“Chai”對象的UnitPrice仍舊是$2,UnitsInStock仍舊是4,當調用SubmitChange()時不會導致任何資料庫UPDATE代碼模組的執行。相似的,在第二個例子中的那些合格Product中只有RecorderLevel原來不是0的才會在SubmitChange()被調用時更新。
插入和刪除樣本
除了更新資料庫中已存在的行之外,LINQ to SQL同樣支援插入和刪除資料。可以通過從DataContext的表集合中添加/移除資料對象,並調用SubmitChange()方法來實現資料庫中的插入和刪除操作。LINQ to SQL也會對添加/移除操作保持跟蹤,併當SubmitChange()被調用時自動執行適當的SQL中的INSERT或DELETE代碼模組。
插入Product
可以通過建立一個新Product執行個體添加一個新的Product到資料庫,設定它的屬性,並添加它到DataContext類中的Product集合中:
當在以上代碼中調用northwind.SubmitChanges()方法時,在資料庫的Product表中會有一條新的記錄被建立。
刪除Product
與添加相似,可以通過從DataContext類中的Product集合中移除某一產品來表達想要從資料庫中刪除相應的記錄:
在以上代碼中先使用LINQ查詢檢索一系列不會被訂購的Product,然後將其傳入DataContext類中Product集合上的RemoveAll()方法。當在以上代碼中調用northwind.SubmitChanges()方法時,這些Product記錄就會從資料庫中的Product表中刪除。
通過關係關聯更新
像LINQ to SQL這樣十分靈活的O/R映射工具,可以讓我們很簡單的通過表之間的關係關聯來對資料模型建模。例如,可以把每個Product建模到一個Category中,每個Order包含多條OrderDetail明細,每條OrderDetail明細都關聯著一個Product,並且每個Customer擁有一組相關聯的Order。
LINQ to SQL能夠讓我們不論是在查詢還是更新資料中都可以利用這些關係關聯。例如,編寫以下代碼來建立一個新Product,並關聯到一個資料庫中已存在的“Beverages”Category上:
注意如何添加Product對象到該Category的Product集合中。這樣就會指明這兩個對象之間存在關係關聯,並會導致LINQ to SQL在調用SubmitChange()時,自動維護兩者之間的主/外鍵關係關聯。
另一個LINQ to SQL有助於管理交叉表關係關聯的例子,讓我們看一下如何對一個現有的Customer建立一個新的Order。在設定Order的OrderDate和RequireDate和Freight後,然後建立兩個Customer訂購的Product相關的OrderDetail,並添加到Order中,隨後關聯Order到Customer上,最後更新所有變化回資料庫:
正如所看到,執行所以這些工作的編程模型是十分清晰並且是物件導向的。
事務
事務是一種通過資料庫(或其他資源管理員)提供的服務,來保證了一系列的單獨的操作是原子性發生的——這就意味著它們要麼全部成功,要麼全部失敗,並且當這一系列操作都自動執行完畢之前,不會有任何更改會發生。當調用DataContext上的SumbitChange()方法時,這些更新操作會封裝到一個事務中。這就意味著當執行多個變化更新時,資料庫永遠不會陷入不一致的狀態——這些變化要麼一起被儲存,要麼任何變化都不儲存。
驗證和商務邏輯
對於處理資料,開發需要考慮的一件十分重要的事就是如何融合資料驗證和商務規則邏輯。LINQ to SQL為開發人員提供了多種途徑,可以十分簡潔地把這些整合到資料模型中。LINQ to SQL會確保一旦添加資料驗證邏輯,就可以無論何時何地在都應用於資料模型上。這就避免了在多處的重複定義,保證了資料模型的可維護性和代碼整潔。
構架驗證支援
當在Visual Studio 2008中使用LINQ to SQL設計器定義資料模型類時,會被預設附加一些根據資料庫表構架推斷出的驗證規則。資料模型類中的屬性的資料類型會與資料庫構架的資料類型相匹配。這就意味著如果試圖指定一個boolean值到decimal,或試圖隱式轉換numeric類型都會導致編譯錯誤。如果資料庫中的列可以為null,則通過LINQ to SQL設計器建立的在資料模型中的相應的屬性會是一個Nullable類型。如果試圖給未標記為Nullable的屬性指定null值,會自動引發異常。相似地,LINQ to SQL也會確保資料庫中的identity/unique列值的正確驗證。
當然也可以使用LINQ to SQL設計器來覆寫這些預設的構架驅動的驗證設定——但是通過預設我們可以自動獲得它們,而且不需要進行額外的工作。LINQ to SQL同樣會自動處理轉義的SQL值,這樣就不必擔心SQL注入攻擊了。
自訂屬性驗證支援
構架驅動的資料驗證僅僅是應用的第一步,但對於實際應用來說還是不夠的。考慮Northwind中的一種情況——Customer類上的Phone屬性,在資料庫中被定義為NVARCHAR類型。開發人員可以使用LINQ to SQL編寫如下代碼更新一個有效電話號碼:
但在應用中會碰到這樣的疑問,如對於下面的代碼,從單純的SQL構架方面來說是合法的:
為防止虛假的電話號碼被添加進資料庫,我們可以添加一個自訂屬性驗證規則到Customer資料模型類中。使用局部類的特性,可以十分簡單的添加規則來驗證電話號碼,只需要添加一個新的包含如下方法定義的局部類到我們的項目中:
以上代碼利用了LINQ to SQL的兩個特性:
1.所以通過LINQ to SQL設計器建立的類都被聲明為局部類——這就意味著開發人員可以十分方便地為這些類添加額外的方法,屬性和事件。這樣對通過LINQ to SQL設計器建立的資料模型類和DataContext類,可以很簡單的擴充自已定義驗證規則和額外的自訂輔助方法。沒有任何設定或後續代碼要求。
2.LINQ to SQL會在資料模型類和DataContext類中暴露了一些自訂的可擴充點,用來在事件發生之前和之後添加驗證邏輯。其中許多的擴充點利用了稱為“partial methods(局部方法)”的新語言特性。
在上面的驗證樣本中,我們使用了在任何設定Customer類上Phone屬性時都會被執行的OnPhoneChanging方法。使用該方法來驗證輸入,如果驗證成功,則從該方法中返回並且LINQ to SQL會認定該值是有效,否則就會在該驗證方法中產生一個異常——防止發生賦值操作。
自訂實體物件驗證支援
屬性層級的驗證對於驗證資料模型類上獨立的屬性是十分有效。但有時卻需要同時驗證一個對象上的多個屬性。考慮這樣一個情境,同時設定Order對象上的OrderDate和RequiredDate屬性:
以上的代碼從單純的SQL構架方面來說是合法的——即使規定交貨日期是訂單下達日期的前一天是毫無意義的。好訊息是現在在Beta2(.NET Framework 3.5)中LINQ to SQL可以十分簡單地添加自訂實體層級的驗證規則來防止類似的錯誤發生。可以為Order實體類添加一個局部類,並實現OnValidate()局部方法,該方法會在實體的值將要記錄進資料庫的時候被調用。使用該驗證方法可以訪問和驗證所有的資料模型類屬性:
使用該驗證方法我們可以檢查實體類上的任何屬性值(即使對於它的管理對象只獲得唯讀訪問),如果驗證的值不正確就可以產生一個異常。從OnValidate()方法中產生的任何異常都導致從資料庫更新操作中退出,並且復原該操作所在事務中的所以更改。
自訂實體插入/更新/刪除方法驗證
在實際應用中,有時我們會需要對於特定的插入,更新或刪除情境來添加驗證邏輯。在Beta2(.NET Framework 3.5)中LINQ to SQL可以通過添加局部類型來擴充DataContext類,然後實現相應的局部方法來為資料模型實體自訂插入,更新和刪除邏輯。當調用DataContext類上的SubmitChanges()方法時,這些方法會被自動調用。
使用這些方法添加適當的驗證邏輯後,如果驗證通過就會通知LINQ to SQL繼續進行資料庫更新操作(通過調用DataContext類中的“ExecuteDynamicXYZ”方法):
適當地添加以上方法後,對於資料對象的任何建立/更新/刪除操作都會自動調用這些方法。例如,考慮這樣一個情境,建立一個新的Order並關聯到一個已存在的Customer上:
當在上面的範例程式碼中調用northwind.SubmitChanges()時,LINQ to SQL會確認是否需要插入一個新的Order對象,並自動調用InsertOrder局部方法。
進階:為事務察看實體變化
在實際應用中,有時不能單純地通過察看單獨的插入/更新/插入操作來添加驗證邏輯,而是應察看一個以事務發生的實體操作變化列表。從.NET Framework 3.5 Beta2開始,我們可以通過調用公用的DataContext.GetChangeSet()方法來訪問這個變化列表。該方法會返回一個暴露每個已作出的添加,修改和移除操作的ChangeSet對象。
還有一種可供選擇的方式是編寫DataContext的子類並覆寫SubmitChanges()方法。然後就可以為更新操作檢索ChangeSet,並執行任何自訂的資料驗證:
使用開放式並行存取執行處理同時發生的變化
在多使用者資料庫系統中開發人員需要考慮的事情之一,就是如何處理對於資料庫中資料同時發生的更新。例如,假設兩個使用者在同一應用程式中檢索同樣的Product對象,一個使用者更新RecorderLevel為0,而另一個更新為1。如果兩個使用者都要試圖儲存Product的更新到資料庫,那麼開發人員就需要決定如何處理變化衝突。
一種方式是“讓後來的更新者獲勝”——這就意味者先來的使用者提交的值會在重大使用者沒有察覺的情況下丟失。這通常被認為是一種缺乏應用程式開發經驗的一種表現。
另一種而且是LINQ to SQL支援的方式是使用開放式並行存取執行模型——如何資料庫中的原始值已經有其他更新操作事先要執行,LINQ to SQL會自動檢測發生地點。LINQ to SQL可以提供一個更改值的衝突列表給開發人員,並且能夠調和分歧,或者是通過UI通知應用程式終端使用者來讓他們決定如何處理。
為插入/更新/刪除情境使用預存程序或自訂SQL邏輯
對於資料庫開發人員來說,好訊息是LINQ to SQL提供了相當靈活的開發模型,可以讓開發人員覆寫通過LINQ to SQL自動執行的動態SQL,並替代為調用自訂的插入,更新,刪除用的預存程序。
一開始的時候,我們可以定義資料模型並使用LINQ to SQL自動處理插入,更新,刪除等邏輯。在稍後還可以自訂用於更新資料模型的預存程序或SQL——不需要對使用資料模型的應用程式邏輯作出任何改變,也不需要對相應的資料驗證或商務規則邏輯作出任何改變。這就為應用程式的構建提供了大量的靈活性。
總結
除了一般的插入,更新,刪除操作,以及資料驗證外,通過這篇Post我們還應注意以下一些要點:
1.善於利用表之間的關係關聯進行資料更新
2.資料驗證和商務邏輯驗證的應用層級包括:構架級,實體級,屬性級,更新操作時驗證和以事務為單位的驗證
3.LINQ to SQL還可以有效地處理SQL注入攻擊
4.LINQ to SQL還可以自動處理事務
5.LINQ to SQL使用開放式並行存取執行模型處理更新衝突