9-6. 管理斷開時的併發問題想要確保只接受在WCF客戶端併發令牌未被修改的實體。解決方案我們有一個如Figure 9-6所示的模型.Figure 9-6訂單實體模型我們想通過WCF服務來更新一個order ,而要確保這個order 在我們上次取回後沒有發生過其它修改。稍後我們將演示兩個不同的方式....
9-6. 管理斷開時的併發
問題
想要確保只接受在WCF客戶端併發令牌未被修改的實體。
解決方案
我們有一個如Figure 9-6所示的模型.
Figure 9-6訂單實體模型
我們想通過WCF服務來更新一個order ,而要確保這個order 在我們上次取回後沒有發生過其它修改。
稍後我們將演示兩個不同的方式.兩種都用一個TimeStamp列作併發控制。
1. 新建一個WCF服務庫,右擊解決方案,選擇“新建項目”,選擇WCF ➤ WCF 服務庫,並命名為Recipe6.
2. 右擊這個項目,選擇“添加” ➤“新建項” ,選擇 數據➤ ADO.NET實體數據模型.在嚮導中選擇Order表. 在EF模型設計圖中,右擊TimeStamp 屬性, select “屬性“, 把“併發模式”設為Fixed.
3. 用Listing 9-30里的代碼定義IService1.cs 文件里的服務
Listing 9-30. Our WCF Service Contract
[ServiceContract]
public interface IService1
{
[OperationContract]
Order InsertOrder();
[OperationContract]
void UpdateOrderWithoutRetrieving(Order order);
[OperationContract]
void UpdateOrderByRetrieving(Order order);
}
4. 用Listing 9-31的代碼實現Service1.cs 文件里的服務。
Listing 9-31. The Implementation of Our Service Contract
public class Service1 : IService1
{
public Order InsertOrder()
{
using (var context=new EFRecipesEntities())
{
//刪除之前的測試數據
context.Database.ExecuteSqlCommand("delete from chapter9.[order]");
var order = new Order { Product = "Camping Tent", Quantity = 3, Status = "Received" };
context.Orders.Add(order);
context.SaveChanges();
return order;
}
}
public void UpdateOrderWithoutRetrieving(Order order)
{
using (var context=new EFRecipesEntities())
{
try
{
context.Orders.Attach(order);
if (order.Status=="Received")
{
context.Entry(order).Property(x => x.Quantity).IsModified = true;
context.SaveChanges();
}
}
catch (OptimisticConcurrencyException ex)
{
//處理併發異常
}
}
}
public void UpdateOrderByRetrieving(Order order)
{
using (var context=new EFRecipesEntities())
{
//從資料庫中獲取當前實體
var dbOrder = context.Orders.Single(o => o.OrderId == order.OrderId);
//執行併發檢查
if (dbOrder!=null&&StructuralComparisons.StructuralEqualityComparer.Equals(order.TimeStamp,dbOrder.TimeStamp))
{
dbOrder.Quantity = order.Quantity;
context.SaveChanges();
}
else
{
//處理併發問題
}
}
}
}
5. 為測試服務,我們在解決方案里創建一個控制台應用程式.在服務項目上右擊,選擇“調試” ➤啟動新實例,然後在控制項目里添加對這個服務的引用。控制台項目代碼如Listing 9-32 所示.
Listing 9-32. The Client We Use to Test Our WCF Service
class Program
{
static void Main(string[] args)
{
var service = new ServiceReference1.Service1Client();
var order = service.InsertOrder();
order.Quantity = 5;
service.UpdateOrderWithoutRetrieving(order);
order = service.InsertOrder();
order.Quantity = 3;
service.UpdateOrderByRetrieving(order);
}
}
如果在Main() 方法前設置斷點,逐語句執行,將會看到代碼運行入服務端。
它是如何工作的?
InsertOrder() 方法(見Listing 9-31) 刪除之前的測試數據,插入一個新的order, 並且返回包含正確ID值和TimeStamp的order.在我們客戶端,我們用兩個略微不同的方式來更新這個order.
第一種用UpdateOrderWithoutRetrieving()方式:先把從客戶端傳過來的order 附加Attach()到DbContext,然後判斷order狀態是否為“Received”,如果是我們把實體 Quantity 屬性狀態設為 modified然後調用
SaveChanges(). EF會生成一個更新SqL語句,它的Where子句里包含OrderId和TimeStamp.如果SaveChanges()前資料庫中該條數據被修改過,則會產生異常(存儲區更新、插入或刪除語句影響到了意外的行數(0)。實體在載入後可能被修改或刪除。刷新 ObjectStateManager 項。)。我們用try,catch來捕獲異常. 這樣就可以保證客戶端提交過來的Order在運行InsertOrder() 之後,資料庫里對應記錄的數據未被修改過. 其實我們可以在保存前先進行併發檢查,從客戶端回傳的order的TimeStamp屬性值與資料庫的值比較。這就是UpdateOrderByRetrieving() 方法里採用的方式。