亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ASP.NET 2.0中怎么實現開放式并發

發布時間:2021-07-15 16:44:56 來源:億速云 閱讀:202 作者:Leah 欄目:開發技術

這期內容當中小編將會給大家帶來有關ASP.NET 2.0中怎么實現開放式并發,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

第一步:如何實現開放式并發控制

  開放式并發控制能夠確保一條記錄在更新或者刪除時跟它開始這次更新或修改過程時保持一致。例如,當在一個可編輯的GridView里點擊編輯按鈕時,該記錄的原始值從數據庫中讀取出來并顯示在TextBox和其他Web控件中。這些原始的值保存在GridView里。隨后,當用戶完成他的修改并點擊更新按鈕,這些原始值加上修改后的新值發送到業務邏輯層,然后到數據訪問層。數據訪問層必定發出一個SQL語句,它將僅僅更新那些開始編輯時的原始值根數據庫中的值一致的記錄。圖二描述了這些事件發生的順序。

ASP.NET 2.0中怎么實現開放式并發

圖2: 為了更新或刪除能夠成功,原始值必須與數據庫中相應的值一致

  有多種方法可以實現開放式并發控制(查看Peter A. Bromberg的文章  Optmistic Concurrency Updating Logic,從摘要中看到許多選擇)。ADO.NET類型化數據集提供了一種應用,這只需要在配置時勾選上一個CheckBox。使用開發式并發的目的是使類型化數據集的TableAdapter的UPDATE和DELETE語句可以檢測自該記錄加載到DataSet中以來數據庫中的值是否被更改。例如下面的UPDATE語句,當當前數據庫中的值與GridView中開始編輯的原始值一致才更新某個產品的名稱和價格。@ProductName 和 @UnitPrice參數包含的是用戶輸入的新值,而參數@original_ProductName 和 @original_UnitPrice則包含最初點擊編輯按鈕時加載到GridView中的值: 

UPDATE Products SET
  ProductName = @ProductName,
  UnitPrice = @UnitPrice
WHERE
  ProductID = @original_ProductID AND
  ProductName = @original_ProductName AND
  UnitPrice = @original_UnitPrice

  注意:這個UPDATE語句是為了易讀而簡單化了。實際上,在WHERE子句中檢測UnitPrice會比較棘手,這是因為UnitPrice可以包含空值,而NULL = NULL則總是返回False(相應地你必須用IS NULL)。

  除了使用一個不同的UPDATE語句之外,配置TableAdapter使用開放式并發控制還需要修改它直接發送到數據庫的方法。回到我們的第一節,創建一個數據訪問層,這些發送到數據庫的方法接收一列標量的值作為輸入參數(不僅僅是強類型DataRow或DataTable的實例)。當使用開放式并發,直接發送到數據庫的Update() 和 Delete()方法就包含了對應原始值的輸入參數。而且,業務邏輯層中批量方式更新的代碼(Update()的重載,它不僅接受標量值,也接受DataRows 和 DataTables)也要做出相應的更改。

  與其擴展我們現有得數據訪問層表適配器使用開放式并發(同時也必須修改業務邏輯層以協調),不如讓我們創建一個新的類型化數據集NorthwindOptimisticConcurrency,在它里面我們添加一個使用開放式并發的Products表適配器。然后,我們將在業務邏輯層中創建類ProductsOptimisticConcurrencyBLL,它為了支持開放式并發的DAL會有適當的更改。一旦這些基礎工作都已完成,我們就可以創建ASP.NET頁面。

第二步: 創建一個支持開放式并發的數據訪問層

  為了創建一個新的類型化數據集,在App_Code文件夾里的DAL文件夾上右鍵點擊,選擇添加一個新的數據集并命名為NorthwindOptimisticConcurrency。正如我們在第一節中看到過的那樣,系統會自動添加一個表適配器(TableAdapter)到當前的類型化數據集眾,并自動地進入TableAdapter配置向導。在第一屏中,向導提示我們選擇數據庫連接 – 連接到同樣的數據庫Northwind并使用Web.config里設置好的連接字符串NORTHWNDConnectionString。

ASP.NET 2.0中怎么實現開放式并發

圖 3: 連接到同一個數據庫Northwind

下一步,向導提示我們選擇如何訪問數據庫:通過一個指定的SQL語句,創建新的存儲過程,或者使用一個現有的存儲過程。既然我們最初的DAL是使用的是指定SQL查詢語句,這里我們還是使用它。

ASP.NET 2.0中怎么實現開放式并發

圖4: 使用指定SQL語句的方式訪問數據庫

  下一步,進入查詢分析器,返回產品信息。讓我們使用在最初的DAL中產品TableAdapter相同的SQL查詢,它返回產品的所有字段包括產品的供應商和類別名稱。

SELECT  ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
      UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
      (SELECT CategoryName FROM Categories
       WHERE Categories.CategoryID = Products.CategoryID)
       as CategoryName,
      (SELECT CompanyName FROM Suppliers
       WHERE Suppliers.SupplierID = Products.SupplierID)
       as SupplierName
FROM   Products

ASP.NET 2.0中怎么實現開放式并發

圖5:使用在最初的DAL中產品TableAdapter相同的SQL查詢 

  在我們進入下一步之前,點擊“高級選項”按鈕。要讓這個TableAdapter使用開放式并發,僅僅需要勾選上“使用開放式并發”。

ASP.NET 2.0中怎么實現開放式并發

圖6:勾選“使用開放式并發”啟用開放式并發控制

  最后,需要指出的是,該TableAdapter應該同時使用“填充DataTable”和“返回DataTable”兩種要生成的方法;并且,勾選“創建方法以將更新直接發送到數據庫(GenerateDBDirectMethods)”。將返回DataTable的方法名稱從GetData改為GetProducts,使之與我們最初的DAL中的命名規則匹配。

ASP.NET 2.0中怎么實現開放式并發

圖7:讓這個TableAdapter利用所有的數據訪問方式

  完成了配置向導后,該數據集設計器將包含一個強類型的Products DataTable和TableAdapter。讓我們花些時間把該DataTable的名稱Products改為ProductsOptimisticConcurrency,方法是右鍵點擊DataTable的標題欄,從菜單中選擇“重命名”。

ASP.NET 2.0中怎么實現開放式并發

圖8:一個DataTable和TableAdapter已經添加到類型化數據集

  為了看看ProductsOptimisticConcurrency TableAdapter(使用開放式并發)和Products TableAdapter(不使用并發控制)的UPDATE 和 DELETE查詢之間有什么不同,選中該TableAdapter并轉到屬性窗口。在DeleteCommand 和 UpdateCommand 這兩個屬性的 CommandText 子屬性里,我們可以看到調用DAL的update或者delete關聯的方法時發送到數據庫的實際的SQL語法。ProductsOptimisticConcurrency TableAdapter使用的DELETE語句是

DELETE FROM [Products]
  WHERE (([ProductID] = @Original_ProductID)
  AND ([ProductName] = @Original_ProductName)
  AND ((@IsNull_SupplierID = 1 AND [SupplierID] IS NULL)
    OR ([SupplierID] = @Original_SupplierID))
  AND ((@IsNull_CategoryID = 1 AND [CategoryID] IS NULL)
    OR ([CategoryID] = @Original_CategoryID))
  AND ((@IsNull_QuantityPerUnit = 1 AND [QuantityPerUnit] IS NULL)
    OR ([QuantityPerUnit] = @Original_QuantityPerUnit))
  AND ((@IsNull_UnitPrice = 1 AND [UnitPrice] IS NULL)
    OR ([UnitPrice] = @Original_UnitPrice))
  AND ((@IsNull_UnitsInStock = 1 AND [UnitsInStock] IS NULL)
    OR ([UnitsInStock] = @Original_UnitsInStock))
  AND ((@IsNull_UnitsOnOrder = 1 AND [UnitsOnOrder] IS NULL)
    OR ([UnitsOnOrder] = @Original_UnitsOnOrder))
  AND ((@IsNull_ReorderLevel = 1 AND [ReorderLevel] IS NULL)
    OR ([ReorderLevel] = @Original_ReorderLevel))
  AND ([Discontinued] = @Original_Discontinued))

相反,最初的DAL的Products TableAdapter所使用的DELETE語句則簡單得多:

DELETE FROM [Products] WHERE (([ProductID] = @Original_ProductID))

  正如你所看到的,啟用了開發式并發的TableAdapter所使用的DELETE語句里的WHERE子句包含了對表Product每一個字段現有的值與GridView(或者DetailsView,FormView)最后一次加載時的原始值的對比。因為除了ProductID,ProductName, 和Discontinued之外,其他所有字段都可能為NULL值,所以WHERE子句里還包含了額外的參數以及與NULL值恰當的比較。

  在這一節里,我們不會在啟用了開放式并發的數據集里增加其他的DataTable了,因為我們的ASP.NET頁面將僅提供更新和刪除產品信息的功能。然而,我們仍然需要在ProductsOptimisticConcurrency TableAdapter里添加GetProductByProductID(productID) 方法。

  為了實現這一點,在TableAdapter的標題欄(在Fill和GetProducts方法名的上方)上右鍵并從菜單里選擇“添加查詢”。這將啟動TableAdapter查詢配置向導。在TableAdapter的最初配置的基礎上,選擇指定SQL語句來創建GetProductByProductID(productID)方法(見圖四)。因為GetProductByProductID(productID)方法返回指定產品的信息,因此需要指定SQL查詢類型為“SELECT(返回行)”。

ASP.NET 2.0中怎么實現開放式并發

圖9:標記SQL查詢類型為“SELECT(返回行)”

  進入下一步,向導提示我們指定SQL語句,并且與載入TableAdapter默認查詢語句。在現有的查詢語句的基礎上添加WHERE ProductID = @ProductID子句,如圖10:

ASP.NET 2.0中怎么實現開放式并發

圖10:在預載入的查詢語句上添加WHERE子句從而返回特定的產品記錄

最后,把生成的方法重命名為FillByProductID和GetProductByProductID。

ASP.NET 2.0中怎么實現開放式并發

圖11:把生成的方法重命名為FillByProductID和GetProductByProductID

完成這個向導之后,現在這個TableAdapter包含兩個訪問數據的方法:GetProducts(),它返回所有 的產品;和GetProductByProductID(productID),它返回特定的產品。

第三步: 創建一個支持啟用了開放式并發的DAL的業務邏輯層

  我們現有的ProductsBLL類包含批量更新和直接發送數據庫的模式的例子。AddProduct方法和 UpdateProduct重載都使用了批量更新模式,通過一個ProductRow實例發送到TableAdapter的Update方法。另一方面,DeleteProduct方法則使用直接發送到數據庫的模式,調用TableAdapter的Delete(productID)方法。在新的ProductsOptimisticConcurrency TableAdapter里,發送到數據庫的方法現還要求傳入原始的值。例如,Delete方法

  現在要求十個輸入參數:原始的ProductID、ProductName、SupplierID、CategoryID、QuantityPerUnit、UnitPrice、UnitsInStock、UnitsOnOrder、ReorderLevel和Discontinued。它在發送到數據庫的DELETE語句的WHERE子句里使用這些額外的輸入參數,僅僅刪除當前數據庫的值與原始值一致的指定記錄。

  使用批量更新模式時,如果標記給TableAdapter的Update使用的方法沒有更改,那么代碼就需要同時記錄原始值和新的值。然而,與其在我們現有的ProductsBLL類的基礎上試圖使用啟用了開放式并發的DAL,不如讓我們重新創意一個業務邏輯類支持我們新的DAL。在App_Code文件夾下的BLL子文件夾里,添加一個名為ProductsOptimisticConcurrencyBLL的新類。

ASP.NET 2.0中怎么實現開放式并發

圖 12: 添加ProductsOptimisticConcurrencyBLL類到BLL文件夾

然后,在ProductsOptimisticConcurrencyBLL類里添加如下代碼:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindOptimisticConcurrencyTableAdapters;
[System.ComponentModel.DataObject]
public class ProductsOptimisticConcurrencyBLL
{
  private ProductsOptimisticConcurrencyTableAdapter _productsAdapter = null;
  protected ProductsOptimisticConcurrencyTableAdapter Adapter
  {
    get
    {
      if (_productsAdapter == null)
        _productsAdapter = new ProductsOptimisticConcurrencyTableAdapter();
      return _productsAdapter;
    }
  }
  [System.ComponentModel.DataObjectMethodAttribute
  (System.ComponentModel.DataObjectMethodType.Select, true)]
  public NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyDataTable GetProducts()
  {
    return Adapter.GetProducts();
  }
}

  注意在類的聲明開始之前的using NorthwindOptimisticConcurrencyTableAdapters語句。命名空間NorthwindOptimisticConcurrencyTableAdapters包含了類ProductsOptimisticConcurrencyTableAdapter,它提供DAL的方法。并且,在類聲明之前我們還能找到System.ComponentModel.DataObject屬性標志,它指示Visual Studio把該類包含在ObjectDataSource向導的數據對象下拉列表中。

  類ProductsOptimisticConcurrencyBLL的Adapter屬性提供快速訪問ProductsOptimisticConcurrencyTableAdapter類的一個實例,并和我們最初的BLL類(ProductsBLL、CategoriesBLL等等)相似。最后,方法GetProducts()僅僅是調用DAL的GetProdcuts()方法并返回一個ProductsOptimisticConcurrencyDataTable對象,該對象由對應數據庫里每一個產品記錄的ProductsOptimisticConcurrencyRow實例組成。

  使用支持開放式并發的發送到數據庫的模式刪除一個產品記錄

  當使用支持開放式并發的DAL發送到數據庫的模式,方法必須傳入新值和原始值。對刪除來說,這沒有新的值,所以僅僅需要傳入原始值。那么,在我們的BLL里,我們必須接收所有原始值所為輸入參數。讓ProductsOptimisticConcurrencyBLL類的DeleteProduct方法使用這個發送到數據的方法。這意味著此方法必須接受所有的十個產品數據字段作為輸入參數,并傳送這些參數到DAL,如下面的代碼所示:

[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct
  (int original_productID, string original_productName,
  int? original_supplierID, int? original_categoryID,
  string original_quantityPerUnit, decimal? original_unitPrice,
  short? original_unitsInStock, short? original_unitsOnOrder,
  short? original_reorderLevel, bool original_discontinued)
{
  int rowsAffected = Adapter.Delete(original_productID,
                   original_productName,
                   original_supplierID,
                   original_categoryID,
                   original_quantityPerUnit,
                   original_unitPrice,
                   original_unitsInStock,
                   original_unitsOnOrder,
                   original_reorderLevel,
                   original_discontinued);
  // Return true if precisely one row was deleted, otherwise false
  return rowsAffected == 1;
}

  如果這些在GridView(或者是DetailsView、FormView)最后一次加載時的原始值跟用戶點擊刪除按鈕時數據庫中的值不一致, WHERE子句將不能匹配任何數據庫記錄,這就沒有記錄會受到影響。因此,TableAdapter的Delete方法將返回0并且BLL的DeleteProduct方法返回false。

  使用支持開放式并發的批量更新模式修改一個產品記錄

  正如之前注意到的,批量更新模式時用的TableAdapter的Update方法也有同樣的方法聲明為不管是否支持開放式并發。也就是,Update方法可以接受一個DataRow,一批DataRow,一個DataTable,或者一個類型化的數據集。正是因為DataTable在它的DataRow(s)里保留了從原始值到修改后的值這個變化的軌跡使這成為可能。當DAL生成它的UPDATE語句時,參數@original_ColumnName裝入DataRow中的原始值,反之,參數@ColumnName裝入DataRow中修改后的值。

  在類ProductsBLL(我們最初使用的不支持開放式并發DAL的)里,當我們使用批量更新模式更新產品信息時,我們的代碼執行的則是按順序執行下列事件:

1.使用TableAdapter的GetProductByProductID(productID)方法讀取當前數據庫中的產品信息到ProductRow實例
2.在第1步里將新的值賦值到ProductRow實例
3.調用TableAdapter的Update方法,傳入該ProductRow實例

  這一連串的步驟,無論如何都不可能支持開放式并發,因為在第一步中產生的ProductRow是直接從數據庫組裝的,這意味著,DataRow中使用的原始值是當前存在于數據庫中值,而并非開始編輯過程時綁定到GridView的值。相反地,當使用啟用開放式并發的DAL,我們需要修改UpdateProduct方法的重載以使用下面這些步驟:

1.使用TableAdapter的GetProductByProductID(productID)方法讀取當前數據庫中的產品信息到ProductsOptimisticConcurrencyRow實例
2.在第1步里將原始 值賦值到ProductsOptimisticConcurrencyRow實例
3.調用ProductsOptimisticConcurrencyRow實例的AcceptChanges()方法,這指示DataRow目前這些值是“原始”的值
4.將新 的值賦值到ProductsOptimisticConcurrencyRow實例
5.調用TableAdapter的Update方法,傳入該ProductsOptimisticConcurrencyRow實例

  第1步讀取當前數據庫里指定產品記錄的所有字段的值。對更新所有 產品字段的UpdateProduct的重載里,這一步是多余的(因為這些值在第2步中被改寫),而對那些僅僅傳入部分字段值的重載方法來說則是必要的。一旦原始值賦值到ProductsOptimisticConcurrencyRow實例,調用AcceptChanges()方法,這將當前DataRow中的值標記為原始值,這些值將用作UPDATE語句的@original_ColumnNam參數。然后,新的參數值被賦值到ProductsOptimisticConcurrencyRow,最后,調用Update方法,傳入這個DataRow。

  下面這些代碼展示了重載方法UpdateProduct接受所有產品數據字段作為輸入參數。雖然這里沒有展示,實際上從本節教程下載的ProductsOptimisticConcurrencyBLL類里還包含了重載方法UpdateProduct,它僅僅接受產品名稱和單價作為輸入參數。

protected void AssignAllProductValues
  (NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyRow product,
  string productName, int? supplierID, int? categoryID, string quantityPerUnit,
  decimal? unitPrice, short? unitsInStock, short? unitsOnOrder,
  short? reorderLevel, bool discontinued)
{
  product.ProductName = productName;
  if (supplierID == null)
    product.SetSupplierIDNull();
  else
    product.SupplierID = supplierID.Value;
  if (categoryID == null)
    product.SetCategoryIDNull();
  else
    product.CategoryID = categoryID.Value;
  if (quantityPerUnit == null)
    product.SetQuantityPerUnitNull();
  else
    product.QuantityPerUnit = quantityPerUnit;
  if (unitPrice == null)
    product.SetUnitPriceNull();
  else
    product.UnitPrice = unitPrice.Value;
  if (unitsInStock == null)
    product.SetUnitsInStockNull();
  else
    product.UnitsInStock = unitsInStock.Value;
  if (unitsOnOrder == null)
    product.SetUnitsOnOrderNull();
  else
    product.UnitsOnOrder = unitsOnOrder.Value;
  if (reorderLevel == null)
    product.SetReorderLevelNull();
  else
    product.ReorderLevel = reorderLevel.Value;
  product.Discontinued = discontinued;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateProduct(
  // new parameter values
  string productName, int? supplierID, int? categoryID, string quantityPerUnit,
  decimal? unitPrice, short? unitsInStock, short? unitsOnOrder,
  short? reorderLevel, bool discontinued, int productID,
  // original parameter values
  string original_productName, int? original_supplierID, int? original_categoryID,
  string original_quantityPerUnit, decimal? original_unitPrice,
  short? original_unitsInStock, short? original_unitsOnOrder,
  short? original_reorderLevel, bool original_discontinued,
  int original_productID)
{
  // STEP 1: Read in the current database product information
  NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyDataTable products =
    Adapter.GetProductByProductID(original_productID);
  if (products.Count == 0)
    // no matching record found, return false
    return false;
  NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyRow product = products[0];
  // STEP 2: Assign the original values to the product instance
  AssignAllProductValues(product, original_productName, original_supplierID,
    original_categoryID, original_quantityPerUnit, original_unitPrice,
    original_unitsInStock, original_unitsOnOrder, original_reorderLevel,
    original_discontinued);
  // STEP 3: Accept the changes
  product.AcceptChanges();
  // STEP 4: Assign the new values to the product instance
  AssignAllProductValues(product, productName, supplierID, categoryID,
    quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel,
    discontinued);
  // STEP 5: Update the product record
  int rowsAffected = Adapter.Update(product);
  // Return true if precisely one row was updated, otherwise false
  return rowsAffected == 1;
}

第四步: 從ASP.NET頁面把原始值和新值傳入BLL 方法

  完成了DAL和BLL后,剩下的工作就是創建一個能利用系統中內建的開放式并發邏輯的ASP.NET頁面。特別地,數據 Web 服務器控件(GridView,DetailsView或FormView)必須記住它的原始值,并且ObjectDataSource必須同時傳送這兩套值到業務邏輯層。此外,ASP.NET頁面必須加以配置從而適當地處理并發沖突。

  首先,打開EditInsertDelete文件夾中的OptimisticConcurrency.aspx頁面,添加一個GridView控件到設計器,設置它的ID屬性為ProductsGrid。從GridView的職能標記里,選擇創建一個新的ObjectDataSource名為ProductsOptimisticConcurrencyDataSource。既然我們希望這個ObjectDataSource使用支持開放式并發的DAL,就把它配置為使用ProductsOptimisticConcurrencyBLL對象。

ASP.NET 2.0中怎么實現開放式并發

圖 13: 該ObjectDataSource使用ProductsOptimisticConcurrencyBLL對象

  在向導中從下拉列表選擇GetProducts,UpdateProduct,和DeleteProduct方法。對UpdateProduct方法,則使用接受所有產品數據字段的重載。

  配置ObjectDataSource控件的屬性

  完成了向導之后,該ObjectDataSource的聲明標記應該如下:

<asp:ObjectDataSource ID="ProductsOptimisticConcurrencyDataSource" runat="server"
  DeleteMethod="DeleteProduct" OldValuesParameterFormatString="original_{0}"
  SelectMethod="GetProducts" TypeName="ProductsOptimisticConcurrencyBLL"
  UpdateMethod="UpdateProduct">
  <DeleteParameters>
    <asp:Parameter Name="original_productID" Type="Int32" />
    <asp:Parameter Name="original_productName" Type="String" />
    <asp:Parameter Name="original_supplierID" Type="Int32" />
    <asp:Parameter Name="original_categoryID" Type="Int32" />
    <asp:Parameter Name="original_quantityPerUnit" Type="String" />
    <asp:Parameter Name="original_unitPrice" Type="Decimal" />
    <asp:Parameter Name="original_unitsInStock" Type="Int16" />
    <asp:Parameter Name="original_unitsOnOrder" Type="Int16" />
    <asp:Parameter Name="original_reorderLevel" Type="Int16" />
    <asp:Parameter Name="original_discontinued" Type="Boolean" />
  </DeleteParameters>
  <UpdateParameters>
    <asp:Parameter Name="productName" Type="String" />
    <asp:Parameter Name="supplierID" Type="Int32" />
    <asp:Parameter Name="categoryID" Type="Int32" />
    <asp:Parameter Name="quantityPerUnit" Type="String" />
    <asp:Parameter Name="unitPrice" Type="Decimal" />
    <asp:Parameter Name="unitsInStock" Type="Int16" />
    <asp:Parameter Name="unitsOnOrder" Type="Int16" />
    <asp:Parameter Name="reorderLevel" Type="Int16" />
    <asp:Parameter Name="discontinued" Type="Boolean" />
    <asp:Parameter Name="productID" Type="Int32" />
    <asp:Parameter Name="original_productName" Type="String" />
    <asp:Parameter Name="original_supplierID" Type="Int32" />
    <asp:Parameter Name="original_categoryID" Type="Int32" />
    <asp:Parameter Name="original_quantityPerUnit" Type="String" />
    <asp:Parameter Name="original_unitPrice" Type="Decimal" />
    <asp:Parameter Name="original_unitsInStock" Type="Int16" />
    <asp:Parameter Name="original_unitsOnOrder" Type="Int16" />
    <asp:Parameter Name="original_reorderLevel" Type="Int16" />
    <asp:Parameter Name="original_discontinued" Type="Boolean" />
    <asp:Parameter Name="original_productID" Type="Int32" />
  </UpdateParameters>
</asp:ObjectDataSource>

  正如你所看到的,DeleteParameters集合包含了對應ProductsOptimisticConcurrencyBLL類的DeleteProduct方法的每一個輸入參數的Parameter實例。同樣地,UpdateParameters集合也包含了對應UpdateProduct每一個輸入參數的Parameter實例。在先前的那些關于數據修改的教程中,我們在這里都會移除ObjectDataSource的OldValuesParameterFormatString屬性,因為這個屬性需要BLL方法既要求傳入原始值也要求傳入修改后的值。此外,這個屬性還需要對應原始值的輸入參數的名稱。既然我們現在要把原始值傳送到BLL,那就不要 刪除這個屬性。

  注意:OldValuesParameterFormatString屬性的值必須映射到BLL里接收原始值的輸入參數的名稱。因為我們把這些參數命名為original_productName,original_supplierID, 等等,我們可以讓OldValuesParameterFormatString屬性的值依舊是original_{0}。然而如果BLL方法的輸入參數名為的old_productName,old_supplierID等等,那么,你不得不把OldValuesParameterFormatString屬性的值改為old_{0}。為了ObjectDataSource能夠正確地將原始值傳送到BLL方法,還有最后一個屬性需要設置。ObjectDataSource有一個 ConflictDetection屬性,它可以設定為下面的 下面兩個值之一:  

 OverwriteChanges – 默認值; 不將原始值發送到BLL方法相應的輸入參數       

 CompareAllValues – 將原始值發送到BLL方法;當使用開放式并發時使用這一項

  稍花些時間將ConflictDetection屬性設置為CompareAllValues。配置GridView的屬性和字段當正確的配置完ObjectDataSource的屬性后,讓我們把注意力放在GridView的設置上。首先,因為我們希望GridView支持編輯和刪除,因此,從GridView的智能標記中點擊添加新列,從下拉列表中選擇CommandField并勾選上“刪除”和“編輯/更新”。這將增加一個CommandField,它的ShowEditButton和ShowDeleteButton屬性都已設置為true。當綁定ProductsOptimisticConcurrencyDataSource ObjectDataSource,該GridView對應每一個產品數據字段都包含一列。

  雖然這樣的一個GridView可以被編輯,但用戶的體驗將是不可接受的。這沒有對數字欄作格式化處理,也沒有validation控件以確保提供product's name并且unit price、units in stock、units on order、和reorder level的值都是大于零的數字。

  跟我們在之前的給編輯和新增界面增加驗證控件 這一節里所論述的一樣,用戶界面可以通過將綁定列(BoundFields)替換為模板列(TemplateFields)實現自定義。我已經通過以下方式修改了這個GridView和它的編輯界面:

1.刪除ProductID、SupplierName、和CategoryName這幾個綁定列;
2.將ProductName綁定列替換為模板列并添加一個RequiredFieldValidation控件;
3.將CategoryID和SupplierID綁定列替換為模板列,并調整編輯界面,使用DropDownList而不是TextBox。在這些模板列的ItemTemplates里,顯示CategoryName和SupplierName字段;
4.將UnitPrice、UnitsInStock、UnitsOnOrder、和ReorderLevel綁定列替換為模板列并添加CompareValidator控件。因為我們在之前的章節里已經詳細說明了如何完成這些任務,我僅僅把最終的聲明語法列出并把具體執行留給讀者作為練習。

<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False"
  DataKeyNames="ProductID" DataSourceID="ProductsOptimisticConcurrencyDataSource"
  OnRowUpdated="ProductsGrid_RowUpdated">
  <Columns>
    <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
    <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
      <EditItemTemplate>
        <asp:TextBox ID="EditProductName" runat="server"
          Text='<%# Bind("ProductName") %>'></asp:TextBox>
        <asp:RequiredFieldValidator ID="RequiredFieldValidator1"
          ControlToValidate="EditProductName"
          ErrorMessage="You must enter a product name."
          runat="server">*</asp:RequiredFieldValidator>
      </EditItemTemplate>
      <ItemTemplate>
        <asp:Label ID="Label1" runat="server"
          Text='<%# Bind("ProductName") %>'></asp:Label>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
      <EditItemTemplate>
        <asp:DropDownList ID="EditCategoryID" runat="server"
          DataSourceID="CategoriesDataSource" AppendDataBoundItems="true"
          DataTextField="CategoryName" DataValueField="CategoryID"
          SelectedValue='<%# Bind("CategoryID") %>'>
          <asp:ListItem Value=">(None)</asp:ListItem>
        </asp:DropDownList><asp:ObjectDataSource ID="CategoriesDataSource"
          runat="server" OldValuesParameterFormatString="original_{0}"
          SelectMethod="GetCategories" TypeName="CategoriesBLL">
        </asp:ObjectDataSource>
      </EditItemTemplate>
      <ItemTemplate>
        <asp:Label ID="Label2" runat="server"
          Text='<%# Bind("CategoryName") %>'></asp:Label>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
      <EditItemTemplate>
        <asp:DropDownList ID="EditSuppliersID" runat="server"
          DataSourceID="SuppliersDataSource" AppendDataBoundItems="true"
          DataTextField="CompanyName" DataValueField="SupplierID"
          SelectedValue='<%# Bind("SupplierID") %>'>
          <asp:ListItem Value=">(None)</asp:ListItem>
        </asp:DropDownList><asp:ObjectDataSource ID="SuppliersDataSource"
          runat="server" OldValuesParameterFormatString="original_{0}"
          SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
        </asp:ObjectDataSource>
      </EditItemTemplate>
      <ItemTemplate>
        <asp:Label ID="Label3" runat="server"
          Text='<%# Bind("SupplierName") %>'></asp:Label>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit"
      SortExpression="QuantityPerUnit" />
    <asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
      <EditItemTemplate>
        <asp:TextBox ID="EditUnitPrice" runat="server"
          Text='<%# Bind("UnitPrice", "{0:N2}") %>' Columns="8" />
        <asp:CompareValidator ID="CompareValidator1" runat="server"
          ControlToValidate="EditUnitPrice"
          ErrorMessage="Unit price must be a valid currency value without the
          currency symbol and must have a value greater than or equal to zero."
          Operator="GreaterThanEqual" Type="Currency"
          ValueToCompare="0">*</asp:CompareValidator>
      </EditItemTemplate>
      <ItemTemplate>
        <asp:Label ID="Label4" runat="server"
          Text='<%# Bind("UnitPrice", "{0:C}") %>'></asp:Label>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Units In Stock" SortExpression="UnitsInStock">
      <EditItemTemplate>
        <asp:TextBox ID="EditUnitsInStock" runat="server"
          Text='<%# Bind("UnitsInStock") %>' Columns="6"></asp:TextBox>
        <asp:CompareValidator ID="CompareValidator2" runat="server"
          ControlToValidate="EditUnitsInStock"
          ErrorMessage="Units in stock must be a valid number
            greater than or equal to zero."
          Operator="GreaterThanEqual" Type="Integer"
          ValueToCompare="0">*</asp:CompareValidator>
      </EditItemTemplate>
      <ItemTemplate>
        <asp:Label ID="Label5" runat="server"
          Text='<%# Bind("UnitsInStock", "{0:N0}") %>'></asp:Label>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Units On Order" SortExpression="UnitsOnOrder">
      <EditItemTemplate>
        <asp:TextBox ID="EditUnitsOnOrder" runat="server"
          Text='<%# Bind("UnitsOnOrder") %>' Columns="6"></asp:TextBox>
        <asp:CompareValidator ID="CompareValidator3" runat="server"
          ControlToValidate="EditUnitsOnOrder"
          ErrorMessage="Units on order must be a valid numeric value
            greater than or equal to zero."
          Operator="GreaterThanEqual" Type="Integer"
          ValueToCompare="0">*</asp:CompareValidator>
      </EditItemTemplate>
      <ItemTemplate>
        <asp:Label ID="Label6" runat="server"
          Text='<%# Bind("UnitsOnOrder", "{0:N0}") %>'></asp:Label>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Reorder Level" SortExpression="ReorderLevel">
      <EditItemTemplate>
        <asp:TextBox ID="EditReorderLevel" runat="server"
          Text='<%# Bind("ReorderLevel") %>' Columns="6"></asp:TextBox>
        <asp:CompareValidator ID="CompareValidator4" runat="server"
          ControlToValidate="EditReorderLevel"
          ErrorMessage="Reorder level must be a valid numeric value
            greater than or equal to zero."
          Operator="GreaterThanEqual" Type="Integer"
          ValueToCompare="0">*</asp:CompareValidator>
      </EditItemTemplate>
      <ItemTemplate>
        <asp:Label ID="Label7" runat="server"
          Text='<%# Bind("ReorderLevel", "{0:N0}") %>'></asp:Label>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
      SortExpression="Discontinued" />
  </Columns>
</asp:GridView>

  我們已經非常接近于完成一個完整的例子。然而,還有一些細節問題需要我們慢慢解決。另外,我們還需要一些界面,當發生并發沖突時用來提示用戶。

  注意: 為了讓數據Web服務器控件能夠正確地把原始的值傳送到ObjectDataSource(它隨之將其發送到BLL),將GirdView的EnableViewState屬性設置為true(默認值)是至關重要的。如果禁用了視圖狀態,這些原始值在postback的時候將會丟失。

  傳送正確的原始值到ObjectDataSource完成了GridView的配置,還有幾個問題。如果這個ObjectDataSource的ConflictDetection 屬性設置為CompareAllValues (正如我們所做的),它會嘗試復制GridView的原始值到它的Parameter實例。回到圖2查看這個過程的圖解。

  特別需要指出的是,這個GridView的原始值是被指定為雙向綁定的。因此,這些必需的原始值是通過雙向綁定獲取的,并且它們是規定為可改變的格式,這一點很重要。為了看看為什么這一點非常重要,花些時間通過瀏覽器訪問我們的頁面。正如所預料那樣,GridView列出每一個產品,并且每行最左邊的一列都顯示編輯和刪除按鈕。

ASP.NET 2.0中怎么實現開放式并發

圖14: GridView列出所有的產品信息

  如果你點擊任意一行的刪除按鈕,則拋出一個FormatException異常。

ASP.NET 2.0中怎么實現開放式并發

圖15: 嘗試刪除任意一個產品導致FormatException異常

  當ObjectDataSource試圖讀取原始的UnitPrice值引發了一個FormatException異常。因為該模板列將UnitPrice的值限制為貨幣格式(<%# Bind("UnitPrice", "{0:C}") %>),它包含一個貨幣符號,例如$19.95。該FormatException異常發生在ObjectDataSource試圖將字符產轉換成小數。為了繞過此問題,我們有許多種選擇:

1.從模板列里刪除貨幣格式限制。就是說,取代<%# Bind("UnitPrice", "{0:C}") %>,簡單地使用<%# Bind("UnitPrice") %>。下方的價格就是沒有格式化的。
2.在模板列中顯示UnitPrice時格式化為貨幣,但是使用Eval關鍵字實現綁定。記得Eval是實現單向綁定的。我們仍然需要提供UnitPrice的值作為原始的值,因此在模板列里我們依舊需要一個雙向綁定的聲明,但這可以放在一個Visible屬性設置為false的Label服務器控件里。在模板列里我們可以使用下面的標記:

<ItemTemplate>
  <asp:Label ID="DummyUnitPrice" runat="server"
    Text='<%# Bind("UnitPrice") %>' Visible="false"></asp:Label>
  <asp:Label ID="Label4" runat="server"
    Text='<%# Eval("UnitPrice", "{0:C}") %>'></asp:Label>
</ItemTemplate>

3.從模板列里刪除貨幣格式限制,使用 <%# Bind("UnitPrice") %>。在GridView的RowDataBound事件處理里,編碼訪問顯示UnitPrice的值的Label服務器控件并設置其Text屬性為格式化的版本。
4.讓UnitPrice保留貨幣格式化。在GridView的RowDeleting事件處理里,將現存的UnitPrice的原始($19.95)替換為實際的小數值(使用Decimal.Parse)。在前面的 在ASP.NET頁面中處理BLL/DAL異常這一節教程里我們也已經看過如何RowUpdating事件處理里實現類似的功能。 在我的例程里我選擇第二種方法,添加一個隱藏的Label服務器控件,并將它的Text屬性雙向綁定到無格式的UnitPrice值。解決了這個問題之后,再次點擊任意一個產品的刪除按鈕。這一次,當ObjectDataSource嘗試調用BLL的UpdateProduct方法時我們得到一個InvalidOperationException異常。

ASP.NET 2.0中怎么實現開放式并發

圖 16: ObjectDataSource找不到具有它要發送的輸入參數的方法

  仔細看看異常信息,明顯地ObjectDataSource希望調用一個BLL的DeleteProduct方法,此方法包含original_CategoryName和original_SupplierName輸入參數。這是因為CategoryID和SupplierID模板列的ItemTemplate當前是雙向綁定到CategoryName和SupplierName數據字段。作為替換,我們需要包含對CategoryID和SupplierID數據字段的Bind聲明。為了實現這一點,把現有的Bind聲明更改為Eval聲明,并且添加隱藏的Label服務器控件,這些Label的Text屬性使用雙向綁定的方式綁定到CategoryID和SupplierID數據字段,如下所示:

<asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
  <EditItemTemplate>
    ...
  </EditItemTemplate>
  <ItemTemplate>
    <asp:Label ID="DummyCategoryID" runat="server"
      Text='<%# Bind("CategoryID") %>' Visible="False"></asp:Label>
    <asp:Label ID="Label2" runat="server"
      Text='<%# Eval("CategoryName") %>'></asp:Label>
  </ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
  <EditItemTemplate>
    ...
  </EditItemTemplate>
  <ItemTemplate>
    <asp:Label ID="DummySupplierID" runat="server"
      Text='<%# Bind("SupplierID") %>' Visible="False"></asp:Label>
    <asp:Label ID="Label3" runat="server"
      Text='<%# Eval("SupplierName") %>'></asp:Label>
  </ItemTemplate>
</asp:TemplateField>

  通過這些更改,現在我們可以成功地刪除和編輯產品信息了!在第五步里,我們將看看如何驗證刪除時發生并發沖突。但是現在,花幾分鐘嘗試更新和刪除一些記錄,確認在單用戶的情況下更新和刪除能夠正常運作。

第五步: 測試開放式并發支持

  為了驗證并發沖突是否能夠被發現(而不是導致數據被盲目改寫),我們需要打開兩個瀏覽器窗口來訪問這個頁面。在兩個瀏覽窗口里,都點擊產品“Chai”的編輯按鈕。然后,在其中一個窗口修改其名稱為“Chai Tea”并點擊更新。這個更新應該會成功并且GridView回到預編輯狀態,并且該產品的名稱已經改為“Chai Tea”。
而在另一個瀏覽器窗口里,產品名稱域依舊顯示的是“Chai”。在這個瀏覽器窗口,將UnitPrice的值更新為25.00。如果沒有開放式并發支持的話,點擊第二個瀏覽器窗口的更新按鈕將把產品名稱改回“Chai”,從而覆蓋了第一個瀏覽器窗口里所作的修改。然而現在有了開發式并發,當點擊第二個窗口中的更新按鈕時導致了一個DBConcurrencyException異常。

ASP.NET 2.0中怎么實現開放式并發

圖 17: 發現并發沖突,拋出一個DBConcurrencyException異常

  這個DBConcurrencyException異常僅當利用DAL的批量更新模式時會被拋出。直接發送到數據庫的模式則不會引發異常,它僅僅會提示沒有行受到影響。為了舉例說明這個,兩個瀏覽器窗口里的GridView都回到預編輯的狀態。然后,在第一個窗口里,點擊編輯按鈕,把產品名稱從“Chai”改為“Chai Tea”并點擊更新。在第二個窗口里,點擊產品“Chai”的刪除按鈕。點擊刪除按鈕,頁面會傳,GridView調用ObjectDataSource的Delete()方法,然后ObjectDataSource調用ProductsOptimisticConcurrencyBLL類的DeleteProduct方法,傳入原始的值。在第二個瀏覽器窗口里原始的ProductName值是“Chai Tea”,這個值與當前數據庫中相應的ProductName值是不一致的。因此,發送到數據庫的DELETE語句影響0行,因為數據庫中沒有記錄能夠滿足WHERE子句。DeleteProduct方法返回false并且ObjectDataSource的數據重新綁定到GridView控件。

  從最后一個用戶的觀點來看,在第二個瀏覽器窗口里點擊了產品“Chai Tea”的刪除按鈕導致屏幕閃爍,恢復后該產品依舊在,雖然現在它的名稱是“Chai”(在第一個瀏覽器窗口里修改了產品名稱)。如果用戶再次點擊刪除按鈕,這次就能成功刪除,因為GridView的原始的ProductName值(“Chai”)現在能夠與數據庫中相應的值匹配。在這些例子里,用戶的體驗跟理想的狀況還有頗遠的距離。顯然我們在使用批量更新模式時不希望用戶看到DBConcurrencyException異常生硬的詳細信息。并且使用直接發送到數據庫模式的行為也會讓用戶有些疑惑,因為用戶操作失敗了但是沒有準確的提示說明為什么。

  為了補救這兩個小問題,我們可以在頁面上放置一個Label服務器控件,它用來提供為什么更新或刪除失敗的說明。在批量更新模式,我們可以在GridView的post級事件處理里判定是否引發了一個DBConcurrencyException異常,顯示必要的警告標簽。對于直接發送到數據庫的方法,我們可以檢測BLL方法(它對一行或多行產生影響返回true,否則false)的返回值并顯示必要的提示信息。

第六步: 添加提示信息并且在發生并發沖突時顯示

  當一個并發沖突出現時,展現出來的行為取決于是使用DAL的批量更新還是直接發送到數據庫的模式。我們這一節的教程兩種模式都用了,用批量更新模式實現修改,用直接發送到數據庫的方式實現刪除。首先,我們添加兩個Label服務器控件到頁面,它們用來解釋更新或刪除數據時出現的并發沖突。設置Label控件的Visible和EnableViewState屬性為false;這意味一般情況下它們都是隱藏的,除非是那些特別的頁面訪問,在那里它們的Visible屬性通過編碼設置為true。

<asp:Label ID="DeleteConflictMessage" runat="server" Visible="False"
  EnableViewState="False" CssClass="Warning"
  Text="The record you attempted to delete has been modified by another user
      since you last visited this page. Your delete was cancelled to allow
      you to review the other user's changes and determine if you want to
      continue deleting this record." />
<asp:Label ID="UpdateConflictMessage" runat="server" Visible="False"
  EnableViewState="False" CssClass="Warning"
  Text="The record you attempted to update has been modified by another user
      since you started the update process. Your changes have been replaced
      with the current values. Please review the existing values and make
      any needed changes." />

  在設置了它們的Visible、EnabledViewState和Text屬性之外,我們還要把CssClass屬性設置為Warning,這讓標簽顯示大的、紅色的、斜體、加粗的字體。這個CSS Warning 分類是在研究插入、更新和刪除的關聯事件這一節里添加到Styles.css并且定義好的。添加了這些標簽之后,Visual Studio設計器里看起來應該類似于圖18:

ASP.NET 2.0中怎么實現開放式并發

圖 18: 兩個Label控件添加到頁面

  這些Label服務器控件放置到適當的位置后,我們準備好檢測當并發沖突發生時如何判定,在哪個時間點把適當的Label的Visible屬性設置為true并顯示提示信息。

  更新時處理并發沖突

  讓我們首先看看當使用批量更新模式是如何處理并發沖突。因為批量更新模式下的這些沖突導致拋出一個DBConcurrencyException異常,我們需要在ASP.NET頁面中添加代碼來判定更新過程中出現的是否DBConcurrencyException異常。如果是,我們則顯示一個信息向用戶解釋他們的更改沒有被保存,由于別的用戶在他開始編輯和點擊更新按鈕之間的時間里修改了同樣的數據記錄。

  正如我們在在ASP.NET頁面中處理BLL/DAL異常 這一節里看過的那樣,這樣的異常可以在數據Web服務器控件的post級事件處理里被發現和排除。因此,我們需要創建一個GridView的RowUpdated事件的處理,它用來檢測是否拋出了一個DBConcurrencyException異常。這個事件處理通過一個不同的分支區別更新過程中引發的其它異常,如下面的時間處理代碼所示:

protected void ProductsGrid_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
  if (e.Exception != null && e.Exception.InnerException != null)
  {
    if (e.Exception.InnerException is System.Data.DBConcurrencyException)
    {
      // Display the warning message and note that the
      // exception has been handled...
      UpdateConflictMessage.Visible = true;
      e.ExceptionHandled = true;
    }
  }
}

  面對一個DBConcurrencyException異常,該事件處理顯示UpdateConflictMessage Label控件并且指出該異常已經被處理。正確地編寫了這些代碼后,當更新記錄時發生了并發沖突,用戶的更改會丟失,因為他們不能覆蓋同時發生的另一個用戶的更改。特別地,GridView回到預編輯幢白并且綁定到當前數據庫中數據。這將在GridView的行中顯示出別的用戶的更改,而之前這些更改是看不見的。另外,UpdateConflictMessage Label控件將向用戶說明發生了什么。圖19詳細展示了這一連串的事件。

ASP.NET 2.0中怎么實現開放式并發

圖 19: 面對并發沖突,一個用戶的更改丟失了

  注意:作為另一種選擇,與其讓GridView回到預編輯狀態,我們還不如讓GridView停留在編輯狀態,通過設置傳入的GridViewUpdatedEventArgs對象的KeepInEditMode屬性為true。如果你接受這種方法,那么,必須重新綁定數據到GridView(通過調用它的DataBind()方法)從而將其他用戶更改后的值栽入到編輯界面。在這一節的可下載的代碼里,RowUpdated事件處理里有這兩行注悉掉的代碼;僅僅需要啟用這兩行代碼就可以讓GridView在發生了并發沖突之后保留編輯模式。

  響應刪除時的并發沖突

  對于直接發送到數據庫的模式,面對并發沖突時并不會引發異常。然而,數據庫語句不影響任何記錄,因為WHERE子句不能匹配任何記錄。所有在BLL里創建的修改數據的方法都被設計為返回一個布爾值指示它們是否正好影響了一條記錄。因此,為了確定刪除記錄時是否發生了并發沖突,我們可以檢查BLL的DeleteProduct方法的返回值。

  BLL方法的返回值可以在ObjectDataSource的post級事件處理中通過傳入事件處理的ObjectDataSourceStatusEventArgs對象的ReturnValue屬性被檢測。因為我們感興趣的是判斷從DeleteProduct方法返回的結果,我們需要創建一個ObjectDataSource的Deleted事件的事件處理程序。該ReturnValue屬性是object類型的,并且如果在方法可以返回一個值之前引發了異常并且方法被中斷的情況下,它的值也可能為null。所以,我們應該首先確保ReturnValue屬性非空并是個布爾值。若能通過這個檢查,如果ReturnValue是 false我們顯示DeleteConflictMessage Label控件。可以通過下面的代碼完成:

protected void ProductsOptimisticConcurrencyDataSource_Deleted(
  object sender, ObjectDataSourceStatusEventArgs e)
{
  if (e.ReturnValue != null && e.ReturnValue is bool)
  {
    bool deleteReturnValue = (bool)e.ReturnValue;
    if (deleteReturnValue == false)
    {
      // No row was deleted, display the warning message
      DeleteConflictMessage.Visible = true;
    }
  }
}

  面對一個并發沖突,用戶的刪除請求會被取消。GridView被刷新,顯示在用戶載入頁面跟點擊刪除按鈕之間的時間里發生在該記錄上面的更改。當發生這樣的一個沖突,顯示DeleteConflictMessage Label控件,說明發生了什么(見圖20)。

ASP.NET 2.0中怎么實現開放式并發

上述就是小編為大家分享的ASP.NET 2.0中怎么實現開放式并發了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

尚志市| 安乡县| 大关县| 庄浪县| 通江县| 余姚市| 临高县| 修武县| 太白县| 安平县| 上犹县| 纳雍县| 雅江县| 疏勒县| 怀柔区| 武邑县| 娱乐| 安新县| 保德县| 宁波市| 铁岭市| 荣成市| 延津县| 广丰县| 永济市| 连山| 措美县| 临沂市| 元氏县| 河池市| 六安市| 岢岚县| 平邑县| 兴海县| 云和县| 安西县| 安泽县| 柳江县| 日土县| 阿图什市| 莱阳市|