9-5. 刪除一個斷開的實體問題我們要把一個把WCF上取回的對象做上刪除的標誌.解決方案假設我們有如Figure 9-5所示實體的支付與票據的模型.Figure 9-5. 一個支付與票據的模型我們的模型展示了支付記錄與票據的關係。在應用程式中,我們用客戶端與用WC封裝EF數據訪問交互. 在我們的例子...
9-5. 刪除一個斷開的實體
問題
我們要把一個把WCF上取回的對象做上刪除的標誌.
解決方案
假設我們有如Figure 9-5所示實體的支付與票據的模型.
Figure 9-5. 一個支付與票據的模型
我們的模型展示了支付記錄與票據的關係。在應用程式中,我們用客戶端與用WC封裝EF數據訪問交互
. 在我們的例子中,我們要用WCF服務刪除Payment (支付)實體。為儘可能簡單,我們創建一個WCF服務庫並且定義模型:
1.右擊解決方案,選擇新項目,選擇WCF ➤WCF服務庫,並命名為:Recipe5
2. 右擊Recipe5 項目➤添加➤新建項,選擇“數據” ➤ ADO.NET 實體數據模型. 根據嚮導,添加 Invoice 和 Payment 表. 簡單起見,我們刪除了Invoice實體的導航屬性Payments(在Invoice實體的Payments屬性上右擊,選擇“從模型刪除”)右擊Payment 實體的TimeStamp 屬性, 選擇“屬性”, 設置“併發模式”為Fixed. 這樣會使TimeStamp做為併發控制,當刪除或更新實體時,它的值將作為SQL語句的Where條件的一部分,3. 用Listing 9-27里的代碼,改變IService1.cs文件里的服務定義.
Listing 9-27. The Service Contract for Our WCF Service
[ServiceContract]
public interface IService1
{
[OperationContract]
Payment InsertPayment();
[OperationContract]
void DeletePayment(Payment payment);
}
4. 在Service1.cs文件中, 實現服務,如Listing 9-28所示.
Listing 9-28. The Implementation of Our Service Contract
public class Service1 : IService1
{
public Payment InsertPayment()
{
using (var context = new EFRecipesEntities())
{
//刪除之前的測試數據
context.Database.ExecuteSqlCommand("delete from chapter9.payment");
context.Database.ExecuteSqlCommand("delete from chapter9.invoice");
var payment = new Payment
{
Amount = 99.95m,
Invoice = new Invoice { Description = "Auto Repair" }
};
context.Payments.Add(payment);
context.SaveChanges();
return payment;
}
}
public void DeletePayment(Payment payment)
{
using (var context=new EFRecipesEntities())
{
context.Entry(payment).State = EntityState.Deleted;
context.SaveChanges();
}
}
}
5. 為測試服務, 需要一個客戶端. 在解決方案里添加一個新的控制台應用程式項目
. 客戶端代碼如Listing 9-29所示. 在WCF上右擊,調試➤啟動新實例,再在控制台項目上右擊➤添加➤服務引用,然後添加對WCF的引用.
Listing 9-29. A Simple Console Application to Test Our WCF Service
class Program
{
static void Main(string[] args)
{
var client = new ServiceReference1.Service1Client();
var payment = client.InsertPayment();
client.DeletePayment(payment);
}
}
如果你在Main()方法首行代碼前設置斷點,然後調試,“逐語句”執行,可以看到WCF服務的執行
它是如何工作的
在本小節, 我們演示了客戶端調用服務對斷開連接的實體進行操作
.在客戶端,我們用InsertPayment() 方法向資料庫插入一個新的 payment. 該方法返回被插入的
payment. 當然 payment 返回給客戶端後就從DbContext中斷開連接.實際上DbContext可以在不同的進程里,甚至於在不同電腦上,
我們用DeletePayment() 方法來把資料庫中的Payment刪除 . 這個方法中 (見Listing 9-28), 我們調用DbContext 的Entry() 方法,並傳遞一個 Payment參數. 然後把Payment實體的屬性設置為EntityState.Deleted,接著SaveChanges() 方法會生成一條刪除的SQL語句,並把數據從資料庫中刪除. 因為我們用了外鍵關聯, 併發屬性TimeStamp是必需的,EF會在SQL的Where子句中使用到。
用這種方式解決迸發. 會遇到一個問題,當你的POCO類有一個或更多複合型屬性時,因為複合類型,在EF里是被重視的,不能為null,簡單和解決辦法是:為複合類型創建一個虛擬的實例。如果你讓複合類型為null,SaveChanges()方法將會拋出一個異常。
如果在多對1和0..1中使用一個獨立關聯,EF要求它們的實體鍵具有正確的值,以為修改和刪除生成Where子句. 在我們例子里,如果在Invoice和Payment中有一個獨立關聯,我們需要給Invoice的InvoiceId導航設置正確的值,來關聯Invoice實例.這樣Where子句里將會包含PaymentId, TimeStamp, 和InvoiceId.
===================================================================
■■註意:當用EF實例一個N層架構時,應慎重地考慮是否用分配外鍵來關聯相關的實體,獨立關聯比較難實現,並會使你的代碼變得複雜. 對此EF團隊的Arthur Vickers 發佈的whats-the-deal-with-mapping-foreign-keys-using-the-entity-framework 博客,有個很好的解釋.當然也歡迎你細讀他的EF的其它文章.
=======================================================================
如果實體對象包含多個獨立關聯,設置它們就是件乏味的事,你可以簡單地從資料庫重新取回然後再刪除,這樣會使你代碼簡單些,但是當你從資料庫重新取回,EF將重寫這些實體間關係
, 當然你可以有NoTracking 選項關閉context 跟蹤來解決.
如果本小節我們用獨立關聯的方式,為已經載入了Payment 實體做刪除標誌時,EF將會對Payment和相關聯的Invoice做上刪除標誌。
同樣地,結果的SQL名句的Where子句包含PaymentId, TimeStamp, and InvoiceId列。
在獨立關聯在刪除實體的另一個選擇是:預先載入相關聯的實體,然後將整個對象圖傳給WCF或是Web API來進行刪除。就我們的實例,我們可以預先載入Invoice和相關的Payment實體。如果我們要刪除Payment實體,我們需要回傳包含Invoice實體的整個對象給服務。不過這樣會消費更多帶寬有更多序列處處理的時間,所以它可能帶來的好處還比不上消耗。