来实际地感受一下开放式并发控制。在2个浏览器里同时打开OptimisticConcurrency.aspx页面,且都点击第一条记录的编辑按钮。在第一个浏览器里改变产品名称并点“编辑”。浏览器将发生回传,GridView控件又回到“预编辑”状态,显示新的产品名称。
在第2个浏览器里,改变产品的价格(不要改产品名称)后,点“编辑”。发生回传,GridView控件又回到“预编辑”状态,和第1个浏览器显示的结果一样——产品的名称改变了但价格没改变,第2个浏览器做的修改失败了。然而,一切都发生的那么静悄悄,没有任何提示刚才发生了并发冲突!
图7:第2个浏览器所做的修改悄悄的丢失了
第2个浏览器更新失败的原因在于:UPDATE命令中WHERE字句过滤掉了所以的记录,没有影响到任何一行记录(即没找到满足条件的记录)。我们再来看UPDATE 语句:
UPDATE [Products] SET [ProductName] = @ProductName, [UnitPrice] = @UnitPrice, [Discontinued] = @Discontinued WHERE [ProductID] = @original_ProductID AND [ProductName] = @original_ProductName AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL) OR ([UnitPrice] = @original_UnitPrice)) AND [Discontinued] = @original_Discontinued
当第2个浏览器更新记录时,WHERE字句里的原始产品名(即Chai)与当前任意一条记录的产品名不匹配(因为第1个浏览器将Chai改为了Chai Tea)。所以表达式“[ProductName] = @original_ProductName ”返回False,导致更新失败。
注意:删除的原理于此相同。同时打开2个浏览器,第1个先对某个产品作更改,再在第2个浏览器删除该产品,同样是因为原始值与更新后的值不匹配,删除失败。
在最终用户(更新失败的那个)看来,他点“更新”按钮后,GridView控件返回“预编辑”状态,但提交的修改丢失了。然而没有任何直观的提醒表明修改失败。当用户的更新因并发冲突失败时,我们最好提醒用户,比如将GridView控件保持在“编辑”状态。下面我们来看如何实现这一点。
第3步:并发冲突的处理
因为并发冲突拒绝用户的更改,所以当发生并发冲突时最好提示用户。在页面上添加一个Label控件,其ID为ConcurrencyViolationMessage,设置其Text 属性为“You have attempted to update or delete a record that was simultaneously updated by another user. Please review the other user's changes and then redo your update or delete”,设置其CssClass属性为“Warning”,它定义在Styles.css中。最后,把Visible和EnableViewState属性设置为“false” 。这样Label控件将不可见,除非发生了某些回传事件(我们在这些回传事件里指定Label控件的Visible属性为true)
图8:在页面添加一个Label控件用以显示提醒信息
执行更新或删除操作时,当GridView的数据源控件完成更新或删除后,才开始执行GridView控件的RowUpdated和RowDeleted事件处理器(event handler)。我们可以在这些事件处理器里计算影响了多少条记录。假如影响了0条记录,亦即操作失败,我们希望将Label控件ConcurrencyViolationMessage显示出来。
为RowUpdated和RowDeleted事件创建处理器,添加如下代码:
protected void Products_RowUpdated(object sender, GridViewUpdatedEventArgs e) { if (e.AffectedRows == 0) { ConcurrencyViolationMessage.Visible = true; e.KeepInEditMode = true; // Rebind the data to the GridView to show the latest changes Products.DataBind(); } } protected void Products_RowDeleted(object sender, GridViewDeletedEventArgs e) { if (e.AffectedRows == 0) ConcurrencyViolationMessage.Visible = true; }