Реализация оптимистического параллелизма с помощью элемента управления SqlDataSource (VB)Implementing Optimistic Concurrency with the SqlDataSource (VB)

по Скотт Митчеллby Scott Mitchell

Скачивание примера приложения или Загрузка PDF-файлаDownload Sample App or Download PDF

В этом учебнике мы рассмотрим основные принципы управления оптимистичным параллелизмом, а затем рассмотрим, как его реализовать с помощью элемента управления SqlDataSource.In this tutorial we review the essentials of optimistic concurrency control and then explore how to implement it using the SqlDataSource control.

ВведениеIntroduction

В предыдущем учебном курсе мы рассмотрели, как добавлять в элемент управления SqlDataSource функции вставки, обновления и удаления.In the preceding tutorial we examined how to add inserting, updating, and deleting capabilities to the SqlDataSource control. Вкратце, чтобы предоставить эти функции, необходимые для указания соответствующего INSERT, UPDATEили DELETE инструкции SQL в свойствах InsertCommand, UpdateCommandили DeleteCommand, а также соответствующих параметров в коллекциях InsertParameters, UpdateParametersи DeleteParameters.In short, to provide these features we needed to specify the corresponding INSERT, UPDATE, or DELETE SQL statement in the control s InsertCommand, UpdateCommand, or DeleteCommand properties, along with the appropriate parameters in the InsertParameters, UpdateParameters, and DeleteParameters collections. Хотя эти свойства и коллекции можно указать вручную, кнопка "настроить мастер источников данных" дополнительно предлагает флажок "создать INSERT, UPDATEи DELETE инструкции", который автоматически создаст эти инструкции на основе инструкции SELECT.While these properties and collections can be specified manually, the Configure Data Source wizard s Advanced button offers a Generate INSERT, UPDATE, and DELETE statements checkbox that will auto-create these statements based on the SELECT statement.

Вместе с флажком создать INSERT, UPDATEи DELETE в диалоговом окне Дополнительные параметры создания SQL включено использование оптимистичного параллелизма (см. рис. 1).Along with the Generate INSERT, UPDATE, and DELETE statements checkbox, the Advanced SQL Generation Options dialog box includes a Use optimistic concurrency option (see Figure 1). Если этот флажок установлен, WHERE предложения в автоматически сформированных инструкциях UPDATE и DELETE изменяются для выполнения обновления или удаления только в том случае, если данные базовой базы данных не были изменены с момента последней загрузки данных пользователем в сетке.When checked, the WHERE clauses in the autogenerated UPDATE and DELETE statements are modified to only perform the update or delete if the underlying database data hasn't been modified since the user last loaded the data into the grid.

Поддержку оптимистичного параллелизма можно добавить из диалогового окна Дополнительные параметры создания SQL.

Рис. 1. Поддержка оптимистичного параллелизма в диалоговом окне "Дополнительные параметры создания SQL"Figure 1: You Can Add Optimistic Concurrency Support from the Advanced SQL Generation Options Dialog Box

В учебнике реализация оптимистичного параллелизма мы рассмотрели основные принципы управления оптимистичным параллелизмом и как добавить его в ObjectDataSource.Back in the Implementing Optimistic Concurrency tutorial we examined the fundamentals of optimistic concurrency control and how to add it to the ObjectDataSource. В этом учебнике мы воспользуемся основными принципами управления оптимистичным параллелизмом, а затем рассмотрим, как его реализовать с помощью SqlDataSource.In this tutorial we'll retouch on the essentials of optimistic concurrency control and then explore how to implement it using the SqlDataSource.

Обзор оптимистичного параллелизмаA Recap of Optimistic Concurrency

Для веб-приложений, позволяющих нескольким пользователям одновременно изменять или удалять одни и те же данные, существует вероятность, что один пользователь может случайно перезаписать другие изменения.For web applications that allow multiple, simultaneous users to edit or delete the same data, there exists a possibility that one user may accidentally overwrite another s changes. В учебнике реализация оптимистичного параллелизма я предоставил следующий пример:In the Implementing Optimistic Concurrency tutorial I provided the following example:

Предположим, что два пользователя, Цзисунь и SAM, посещали страницу в приложении, которое позволяло посетителям обновлять и удалять продукты с помощью элемента управления GridView.Imagine that two users, Jisun and Sam, were both visiting a page in an application that allowed visitors to update and delete products through a GridView control. Одновременно нажмите кнопку изменить для Chai.Both click the Edit button for Chai around the same time. Цзисунь изменяет название продукта на чай Chai и нажимает кнопку Обновить.Jisun changes the product name to Chai Tea and clicks the Update button. Результатом является UPDATEная инструкция, которая отправляется в базу данных, которая задает все обновляемые поля продукта (несмотря на то, что цзисунь только обновил одно поле, ProductName).The net result is an UPDATE statement that is sent to the database, which sets all of the product s updateable fields (even though Jisun only updated one field, ProductName). На данный момент база данных имеет значения Чай Chai, Категория напитки, поставщик Exotic Liquids и т. д. для этого конкретного продукта.At this point in time, the database has the values Chai Tea, the category Beverages, the supplier Exotic Liquids, and so on for this particular product. Однако на экране GridView on SAM по-прежнему отображается название продукта в редактируемой строке GridView в виде Chai.However, the GridView on Sam s screen still shows the product name in the editable GridView row as Chai. Через несколько секунд после того, как изменения Цзисунь s зафиксированы, Сэм обновляет категорию на «специи» и нажимает кнопку Обновить.A few seconds after Jisun s changes have been committed, Sam updates the category to Condiments and clicks Update. В результате инструкция UPDATE отправляется в базу данных, которая задает имя продукта Chai, CategoryID с соответствующим ИДЕНТИФИКАТОРом категории «специи» и т. д.This results in an UPDATE statement sent to the database that sets the product name to Chai, the CategoryID to the corresponding Condiments category ID, and so on. Изменения, внесенные в цзисунь с названием продукта, были перезаписаны.Jisun s changes to the product name have been overwritten.

Это взаимодействие показано на рис. 2.Figure 2 illustrates this interaction.

, когда два пользователя одновременно обновляют запись a, существует вероятность того, что один пользователь s может перезаписать другиеWhen Two Users Simultaneously Update a Record There s Potential for One User s Changes to Overwrite the Other s

Рис. 2. когда два пользователя одновременно обновляют запись a, существует вероятность того, что один пользователь s может перезаписать другие (щелкните, чтобы просмотреть изображение с полным размером).Figure 2: When Two Users Simultaneously Update a Record There s Potential for One User s Changes to Overwrite the Other s (Click to view full-size image)

Чтобы предотвратить несворачивание этого сценария, необходимо реализовать форму управления параллелизмом .To prevent this scenario from unfolding, a form of concurrency control must be implemented. Оптимистическая блокировка . в этом учебнике рассматривается предположение о том, что при возникновении конфликтов параллелизма, а затем подавляющее большинство времени таких конфликтов не возникает.Optimistic concurrency the focus of this tutorial works on the assumption that while there may be concurrency conflicts every now and then, the vast majority of the time such conflicts won't arise. Таким образом, если возникает конфликт, управление оптимистичным параллелизмом просто информирует пользователя о том, что их изменения можно сохранить, так как другой пользователь изменил те же данные.Therefore, if a conflict does arise, optimistic concurrency control simply informs the user that their changes can t be saved because another user has modified the same data.

Note

Для приложений, в которых предполагается наличие множества конфликтов параллелизма или если такие конфликты не приемлемой, вместо этого можно использовать Пессимистическое управление параллелизмом.For applications where it is assumed that there will be many concurrency conflicts or if such conflicts are not tolerable, then pessimistic concurrency control can be used instead. Дополнительные сведения об управлении пессимистичным параллелизмом см. в статье реализация оптимистичного параллелизма в учебнике.Refer back to the Implementing Optimistic Concurrency tutorial for a more thorough discussion on pessimistic concurrency control.

Управление оптимистичным параллелизмом работает, гарантируя, что обновляемая или Удаляемая запись имеет те же значения, что и при запуске процесса обновления или удаления.Optimistic concurrency control works by ensuring that the record being updated or deleted has the same values as it did when the updating or deleting process started. Например, при нажатии кнопки изменить в редактируемом элементе GridView значения записей считываются из базы данных и отображаются в текстовых полях и других веб-элементах управления.For example, when clicking the Edit button in an editable GridView, the record s values are read from the database and displayed in TextBoxes and other Web controls. Эти исходные значения сохраняются в GridView.These original values are saved by the GridView. Позже, после того, как пользователь вносит изменения и нажимает кнопку "Обновить", в используемой инструкции UPDATE необходимо учитывать исходные значения плюс новые значения, а также обновлять базовую запись базы данных только в том случае, если исходные значения, которые пользователь запустил редактирование, идентичны значениям в базе данных.Later, after the user makes her changes and clicks the Update button, the UPDATE statement used must take into account the original values plus the new values and only update the underlying database record if the original values that the user started editing are identical to the values still in the database. На рис. 3 показана эта последовательность событий.Figure 3 depicts this sequence of events.

для успешности обновления или удаления исходные значения должны быть равны текущим значениям базы данных.For the Update or Delete to Succeed, the Original Values Must Be Equal to the Current Database Values

Рис. 3. для успешности обновления или удаления исходные значения должны быть равны текущим значениям базы данных (щелкните, чтобы просмотреть изображение с полным размером).Figure 3: For the Update or Delete to Succeed, the Original Values Must Be Equal to the Current Database Values (Click to view full-size image)

Существует множество подходов к реализации оптимистичного параллелизма (см. Питер. статье логику обновления оптимистичного параллелизма для краткого взгляда на ряд параметров).There are various approaches to implementing optimistic concurrency (see Peter A. Bromberg's Optimistic Concurrency Updating Logic for a brief look at a number of options). Методика, используемая SqlDataSource (а также ADO.NET типизированными наборами данных, используемыми в нашем уровне доступа к данным) дополняет предложение WHERE для включения сравнения всех исходных значений.The technique used by the SqlDataSource (as well as by the ADO.NET Typed DataSets used in our Data Access Layer) augments the WHERE clause to include a comparison of all of the original values. Следующая инструкция UPDATE, например, обновляет имя и цену продукта, только если значения текущей базы данных равны значениям, первоначально полученным при обновлении записи в GridView.The following UPDATE statement, for example, updates the name and price of a product only if the current database values are equal to the values that were originally retrieved when updating the record in the GridView. Параметры @ProductName и @UnitPrice содержат новые значения, введенные пользователем, тогда как @original_ProductName и @original_UnitPrice содержат значения, которые были первоначально загружены в GridView при нажатии кнопки "Изменить":The @ProductName and @UnitPrice parameters contain the new values entered by the user, whereas @original_ProductName and @original_UnitPrice contain the values that were originally loaded into the GridView when the Edit button was clicked:

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

Как мы увидим в этом руководстве, включение управления оптимистичным параллелизмом с помощью SqlDataSource — это просто проверка флажка.As we'll see in this tutorial, enabling optimistic concurrency control with the SqlDataSource is as simple as checking a checkbox.

Шаг 1. Создание SqlDataSource, поддерживающего оптимистичный параллелизмStep 1: Creating a SqlDataSource that Supports Optimistic Concurrency

Для начала откройте страницу OptimisticConcurrency.aspx из папки SqlDataSource.Start by opening the OptimisticConcurrency.aspx page from the SqlDataSource folder. Перетащите элемент управления SqlDataSource из панели элементов в конструктор, заID его свойство для ProductsDataSourceWithOptimisticConcurrency.Drag a SqlDataSource control from the Toolbox onto the Designer, settings its ID property to ProductsDataSourceWithOptimisticConcurrency. Затем щелкните ссылку Configure Data Source (настроить источник данных) в смарт-теге элемента управления.Next, click on the Configure Data Source link from the control s smart tag. На первом экране мастера выберите работа с NORTHWINDConnectionString и нажмите кнопку Далее.From the first screen in the wizard, choose to work with the NORTHWINDConnectionString and click Next.

выбрать работу с NORTHWINDConnectionStringChoose to Work with the NORTHWINDConnectionString

Рис. 4. Выбор режима работы с NORTHWINDConnectionString (щелкните, чтобы просмотреть изображение с полным размером)Figure 4: Choose to Work with the NORTHWINDConnectionString (Click to view full-size image)

В этом примере мы добавим элемент управления GridView, который позволяет пользователям редактировать таблицу Products.For this example we'll be adding a GridView that enables users to edit the Products table. Поэтому на экране Настройка инструкции Select выберите таблицу Products из раскрывающегося списка и выберите столбцы ProductID, ProductName, UnitPriceи Discontinued, как показано на рис. 5.Therefore, from the Configure the Select Statement screen, choose the Products table from the drop-down list and select the ProductID, ProductName, UnitPrice, and Discontinued columns, as shown in Figure 5.

из таблицы Products возвращает столбцы ProductID, ProductName, UnitPrice и неподдерживаемыеFrom the Products Table, Return the ProductID, ProductName, UnitPrice, and Discontinued Columns

Рис. 5. из таблицы Products возвратить столбцы ProductID, ProductName, UnitPriceи Discontinued (щелкните,чтобы просмотреть изображение с полным размером)Figure 5: From the Products Table, Return the ProductID, ProductName, UnitPrice, and Discontinued Columns (Click to view full-size image)

После выбора столбцов нажмите кнопку Дополнительно, чтобы открыть диалоговое окно Дополнительные параметры создания SQL.After picking the columns, click the Advanced button to bring up the Advanced SQL Generation Options dialog box. Установите флажки создать INSERT, UPDATEи DELETE и использовать флажки оптимистичного параллелизма и нажмите кнопку ОК (см. рис. 1 на снимке экрана).Check the Generate INSERT, UPDATE, and DELETE statements and Use optimistic concurrency checkboxes and click OK (refer back to Figure 1 for a screenshot). Завершите работу мастера, нажав кнопку Далее, а затем Готово.Complete the wizard by clicking Next, then Finish.

После завершения работы мастера настройки источника данных изучите результирующие DeleteCommand и UpdateCommand свойства и коллекции DeleteParameters и UpdateParameters.After completing the Configure Data Source wizard, take a moment to examine the resulting DeleteCommand and UpdateCommand properties and the DeleteParameters and UpdateParameters collections. Самый простой способ сделать это — щелкнуть вкладку Источник в левом нижнем углу, чтобы увидеть декларативный синтаксис страницы.The easiest way to do this is to click on the Source tab in the lower left corner to see the page s declarative syntax. Здесь вы увидите UpdateCommand значение:There you will find an UpdateCommand value of:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

С семью параметрами в коллекции UpdateParameters:With seven parameters in the UpdateParameters collection:

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
      ...
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
    ...
</asp:SqlDataSource>

Аналогичным образом, свойства DeleteCommand и DeleteParameters Collection должны выглядеть следующим образом:Similarly, the DeleteCommand property and DeleteParameters collection should look like the following:

DELETE FROM [Products]
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        ...
    </UpdateParameters>
    ...
</asp:SqlDataSource>

Помимо расширения WHEREных предложений свойств UpdateCommand и DeleteCommand (и добавления дополнительных параметров в соответствующие коллекции параметров), выбор параметра использовать оптимистичный параллелизм корректирует два других свойства:In addition to augmenting the WHERE clauses of the UpdateCommand and DeleteCommand properties (and adding the additional parameters to the respective parameter collections), selecting the Use optimistic concurrency option adjusts two other properties:

Когда веб-элемент управления данными вызывает метод SqlDataSource Update() или Delete(), он передает исходные значения.When the data Web control invokes the SqlDataSource s Update() or Delete() method, it passes in the original values. Если свойство ConflictDetection SqlDataSource s имеет значение CompareAllValues, эти исходные значения добавляются в команду.If the SqlDataSource s ConflictDetection property is set to CompareAllValues, these original values are added to the command. Свойство OldValuesParameterFormatString предоставляет шаблон именования, используемый для исходных параметров значения.The OldValuesParameterFormatString property provides the naming pattern used for these original value parameters. Мастер настройки источников данных использует исходный_{0} и называет каждый исходный параметр в свойствах UpdateCommand и DeleteCommand и соответственно UpdateParameters и DeleteParameters коллекций.The Configure Data Source wizard uses original_{0} and names each original parameter in the UpdateCommand and DeleteCommand properties and UpdateParameters and DeleteParameters collections accordingly.

Note

Так как мы не используем возможности вставки элементов управления SqlDataSource, вы можете удалить свойство InsertCommand и его InsertParametersную коллекцию.Since we re not using the SqlDataSource control s inserting capabilities, feel free to remove the InsertCommand property and its InsertParameters collection.

Правильная обработка значенийNULLCorrectly HandlingNULLValues

К сожалению, дополненные UPDATE и DELETE инструкции, автоматически созданные мастером настройки источника данных при использовании оптимистичного параллелизма, не работают с записями, содержащими значения NULL.Unfortunately, the augmented UPDATE and DELETE statements autogenerated by the Configure Data Source wizard when using optimistic concurrency do not work with records that contain NULL values. Чтобы узнать, почему, рассмотрим наш UpdateCommandSqlDataSource:To see why, consider our SqlDataSource s UpdateCommand:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

Столбец UnitPrice в таблице Products может иметь NULL значения.The UnitPrice column in the Products table can have NULL values. Если определенная запись имеет NULL значение для UnitPrice, то WHERE предложение [UnitPrice] = @original_UnitPrice всегда будет иметь значение false, так как NULL = NULL всегда возвращает значение false.If a particular record has a NULL value for UnitPrice, the WHERE clause portion [UnitPrice] = @original_UnitPrice will always evaluate to False because NULL = NULL always returns False. Поэтому записи, содержащие NULL значения, не могут быть изменены или удалены, так как операторы UPDATE и DELETE WHERE предложения не возвращают строк для обновления или удаления.Therefore, records that contain NULL values cannot be edited or deleted, as the UPDATE and DELETE statements WHERE clauses won't return any rows to update or delete.

Note

Эта ошибка впервые была передана в корпорацию Майкрософт в июне 2004 в SqlDataSource создает неверные инструкции SQL и сообщает о том, что в следующей версии ASP.NET запланировано исправление отчета.This bug was first reported to Microsoft in June of 2004 in SqlDataSource Generates Incorrect SQL Statements and is reportedly scheduled to be fixed in the next version of ASP.NET.

Чтобы устранить эту проблему, необходимо вручную обновить предложения WHERE в свойствах UpdateCommand и DeleteCommand для всех столбцов, которые могут иметь NULL значения.To fix this, we have to manually update the WHERE clauses in both the UpdateCommand and DeleteCommand properties for all columns that can have NULL values. В общем случае измените [ColumnName] = @original_ColumnName на:In general, change [ColumnName] = @original_ColumnName to:

(
   ([ColumnName] IS NULL AND @original_ColumnName IS NULL)
     OR
   ([ColumnName] = @original_ColumnName)
)

Это изменение можно выполнить непосредственно через декларативную разметку с помощью параметров Упдатекуери или Делетекуери окно свойств или на вкладках "Обновить" и "Удалить" в параметре "указать пользовательскую инструкцию SQL или хранимую процедуру" в разделе "Настройка данных". Мастер источников.This modification can be made directly through the declarative markup, via the UpdateQuery or DeleteQuery options from the Properties window, or through the UPDATE and DELETE tabs in the Specify a custom SQL statement or stored procedure option in the Configure Data Source wizard. Опять же, это изменение необходимо выполнить для каждого столбца в предложении UpdateCommand и DeleteCommand s WHERE, который может содержать значения NULL.Again, this modification must be made for every column in the UpdateCommand and DeleteCommand s WHERE clause that can contain NULL values.

Применение этого примера к нашему примеру приводит к следующим измененным UpdateCommand и DeleteCommandным значениям:Applying this to our example results in the following modified UpdateCommand and DeleteCommand values:

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
DELETE FROM [Products]
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. Добавление элемента GridView с параметрами правки и удаленияStep 2: Adding a GridView with Edit and Delete Options

При настройке SqlDataSource для поддержки оптимистичного параллелизма остается только добавить веб-элемент управления данными на страницу, использующую этот элемент управления параллелизмом.With the SqlDataSource configured to support optimistic concurrency, all that remains is to add a data Web control to the page that utilizes this concurrency control. В этом руководстве мы добавим GridView, который предоставляет функции редактирования и удаления.For this tutorial, let s add a GridView that provides both edit and delete functionality. Для этого перетащите элемент GridView с панели элементов в конструктор и задайте для его ID значение Products.To accomplish this, drag a GridView from the Toolbox onto the Designer and set its ID to Products. В смарт-теге GridView s привяжите его к элементу управления ProductsDataSourceWithOptimisticConcurrency SqlDataSource, добавленному на шаге 1.From the GridView s smart tag, bind it to the ProductsDataSourceWithOptimisticConcurrency SqlDataSource control added in Step 1. Наконец, установите флажки Включить редактирование и включить удаление из смарт-тега.Finally, check the Enable Editing and Enable Deleting options from the smart tag.

привязать GridView к SqlDataSource и разрешить редактирование и удалениеBind the GridView to the SqlDataSource and Enable Editing and Deleting

Рис. 6. привязка элемента управления GridView к SqlDataSource и включение редактирования и удаления (щелкните, чтобы просмотреть изображение с полным размером)Figure 6: Bind the GridView to the SqlDataSource and Enable Editing and Deleting (Click to view full-size image)

После добавления GridView настройте его внешний вид, удалив ProductID BoundField, изменив свойство ProductName BoundField HeaderText на Product и обновив BoundField UnitPrice, чтобы его свойство HeaderText было просто ценой.After adding the GridView, configure its appearance by removing the ProductID BoundField, changing the ProductName BoundField s HeaderText property to Product, and updating the UnitPrice BoundField so that its HeaderText property is simply Price. В идеале мы улучшаем интерфейс правки, чтобы включить RequiredFieldValidator для значения ProductName и CompareValidator для UnitPriceного значения (чтобы убедиться, что оно имеет правильно отформатированное числовое значение).Ideally, we d enhance the editing interface to include a RequiredFieldValidator for the ProductName value and a CompareValidator for the UnitPrice value (to ensure it s a properly formatted numeric value). Более подробные сведения о настройке интерфейса редактирования GridView s см. в руководстве по настройке интерфейса изменения данных .Refer to the Customizing the Data Modification Interface tutorial for a more in-depth look at customizing the GridView s editing interface.

Note

Состояние представления GridView s должно быть включено, так как исходные значения, переданные из GridView в SqlDataSource, хранятся в состоянии представления.The GridView s view state must be enabled since the original values passed from the GridView to the SqlDataSource are stored in view state.

После внесения этих изменений в GridView декларативная разметка GridView и SqlDataSource должна выглядеть следующим образом:After making these modifications to the GridView, the GridView and SqlDataSource declarative markup should look similar to the following:

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ConflictDetection="CompareAllValues"
    ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
    DeleteCommand=
        "DELETE FROM [Products]
         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"
    OldValuesParameterFormatString=
        "original_{0}"
    SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
         FROM [Products]"
    UpdateCommand=
        "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">
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
</asp:SqlDataSource>
<asp:GridView ID="Products" runat="server"
    AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="ProductsDataSourceWithOptimisticConcurrency">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price"
            SortExpression="UnitPrice" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>

Чтобы увидеть управление оптимистичным параллелизмом в действии, откройте два окна браузера и загрузите страницу OptimisticConcurrency.aspx.To see the optimistic concurrency control in action, open two browser windows and load the OptimisticConcurrency.aspx page in both. Нажмите кнопки "Изменить" для первого продукта в обоих браузерах.Click on the Edit buttons for the first product in both browsers. В одном браузере измените название продукта и нажмите кнопку Обновить.In one browser, change the product name and click Update. Браузер выполнит обратную передачу, и GridView вернется в режим предварительного редактирования, отображая новое название продукта для только что измененной записи.The browser will postback and the GridView will return to its pre-editing mode, showing the new product name for the record just edited.

Во втором окне браузера измените цену (но оставьте имя продукта исходным значением) и нажмите кнопку Обновить.In the second browser window, change the price (but leave the product name as its original value) and click Update. При обратной передаче сетка возвращается в режим предварительного редактирования, но изменение цены не записывается.On postback, the grid returns to its pre-editing mode, but the change to the price is not recorded. Второй браузер отображает то же самое значение, что и первое название продукта со старой ценой.The second browser shows the same value as the first one the new product name with the old price. Изменения, внесенные во втором окне браузера, были потеряны.The changes made in the second browser window were lost. Более того, изменения были потеряны, так как не существовало исключений или сообщений, указывающих на возникновение одновременного нарушения.Moreover, the changes were lost rather quietly, as there was no exception or message indicating that a concurrency violation just occurred.

изменения во втором окне браузера были автоматически потеряныThe Changes in the Second Browser Window Were Silently Lost

Рис. 7. изменения во втором окне браузера были автоматически потеряны (щелкните, чтобы просмотреть изображение с полным размером)Figure 7: The Changes in the Second Browser Window Were Silently Lost (Click to view full-size image)

Причина, по которой изменения во втором браузере не были зафиксированы, заключается в том, что предложение UPDATE инструкции s WHERE отфильтровывает все записи и, следовательно, не влияет на строки.The reason why the second browser s changes were not committed was because the UPDATE statement s WHERE clause filtered out all records and therefore did not affect any rows. Давайте снова рассмотрим инструкцию UPDATE:Let s look at the UPDATE statement again:

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

Когда второе окно браузера обновляет запись, исходное название продукта, указанное в предложении WHERE, не будет соответствовать существующему названию продукта (поскольку оно было изменено первым браузером).When the second browser window updates the record, the original product name specified in the WHERE clause doesn t match up with the existing product name (since it was changed by the first browser). Таким образом, инструкция [ProductName] = @original_ProductName возвращает значение false, а UPDATE не влияет на какие бы то ни было записи.Therefore, the statement [ProductName] = @original_ProductName returns False, and the UPDATE does not affect any records.

Note

Функция удаления работает аналогичным образом.Delete works in the same manner. Откройте два окна браузера, начните с изменения определенного продукта, а затем сохранив его изменения.With two browser windows open, start by editing a given product with one, and then saving its changes. После сохранения изменений в одном браузере нажмите кнопку Удалить для того же продукта в другом.After saving the changes in the one browser, click the Delete button for the same product in the other. Поскольку исходные значения не соответствуют t в предложении WHERE оператора DELETE, удаление не выполняется автоматически.Since the original values don t match up in the DELETE statement s WHERE clause, the delete silently fails.

В перспективе "конечные пользователи" во втором окне браузера после нажатия кнопки "Обновить" Сетка возвращается в режим предварительного редактирования, но изменения были потеряны.From the end user s perspective in the second browser window, after clicking the Update button the grid returns to the pre-editing mode, but their changes were lost. Однако нет визуальных отзывов о том, что их изменения не были внесены.However, there s no visual feedback that their changes didn't stick. В идеале, если изменения пользователя теряются до нарушения параллелизма, мы будем уведомлять их и, возможно, сохраняют сетку в режиме редактирования.Ideally, if a user s changes are lost to a concurrency violation, we d notify them and, perhaps, keep the grid in edit mode. Давайте посмотрим, как это сделать.Let s look at how to accomplish this.

Шаг 3. Определение времени возникновения одновременного нарушенияStep 3: Determining When a Concurrency Violation Has Occurred

Так как нарушение параллелизма отклоняет сделанные изменения, было бы неплохо оповещать пользователя при возникновении одновременного нарушения.Since a concurrency violation rejects the changes one has made, it would be nice to alert the user when a concurrency violation has occurred. Чтобы предупредить пользователя, добавим элемент управления Label в верхнюю часть страницы с именем ConcurrencyViolationMessage, свойство Text которого отображает следующее сообщение: предпринята попытка обновить или удалить запись, которая одновременно обновлялась другим пользователем.To alert the user, let s add a Label Web control to the top of the page named ConcurrencyViolationMessage whose Text property displays the following message: 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 свойство значение предупреждение, которое является классом CSS, определенным в Styles.css, который отображает текст красным, курсивным, полужирным и крупным шрифтом.Set the Label control s CssClass property to Warning, which is a CSS class defined in Styles.css that displays text in a red, italic, bold, and large font. Наконец, задайте метку s Visible и свойства EnableViewState для False.Finally, set the Label s Visible and EnableViewState properties to False. Это приведет к скрытию метки только для тех обратных передач, в которых для свойства Visible задано значение True.This will hide the Label except for only those postbacks where we explicitly set its Visible property to True.

добавить на страницу элемент управления Label для вывода предупрежденияAdd a Label Control to the Page to Display the Warning

Рис. 8. Добавление на страницу элемента управления Label для отображения предупреждения (щелкните, чтобы просмотреть изображение с полным размером)Figure 8: Add a Label Control to the Page to Display the Warning (Click to view full-size image)

При выполнении Update или DELETE обработчики событий GridView RowUpdated и RowDeleted срабатывают после того, как элемент управления источником данных выполнил запрошенное обновление или удалить.When performing an update or delete, the GridView s RowUpdated and RowDeleted event handlers fire after its data source control has performed the requested update or delete. Мы можем определить, сколько строк было затронуто операцией из этих обработчиков событий.We can determine how many rows were affected by the operation from these event handlers. Если затронутые нулевые строки, необходимо отобразить ConcurrencyViolationMessage метку.If zero rows were affected, we want to display the ConcurrencyViolationMessage Label.

Создайте обработчик событий для событий RowUpdated и RowDeleted и добавьте следующий код:Create an event handler for both the RowUpdated and RowDeleted events and add the following code:

Protected Sub Products_RowUpdated(sender As Object, e As GridViewUpdatedEventArgs) _
    Handles Products.RowUpdated
    If e.AffectedRows = 0 Then
        ConcurrencyViolationMessage.Visible = True
        e.KeepInEditMode = True
        ' Rebind the data to the GridView to show the latest changes
        Products.DataBind()
    End If
End Sub
Protected Sub Products_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
    Handles Products.RowDeleted
    If e.AffectedRows = 0 Then
        ConcurrencyViolationMessage.Visible = True
    End If
End Sub

В обоих обработчиках событий мы проверяем свойство e.AffectedRows и, если оно равно 0, задайте для свойства ConcurrencyViolationMessage метка s Visible значение True.In both event handlers we check the e.AffectedRows property and, if it equals 0, set the ConcurrencyViolationMessage Label s Visible property to True. В обработчике событий RowUpdated мы также предложите элементу управления GridView оставаться в режиме редактирования, установив для свойства KeepInEditMode значение true.In the RowUpdated event handler, we also instruct the GridView to stay in edit mode by setting the KeepInEditMode property to true. При этом необходимо повторно привязать данные к сетке, чтобы другие пользовательские данные загружались в интерфейс редактирования.In doing so, we need to rebind the data to the grid so that the other user s data is loaded into the editing interface. Это достигается путем вызова метода DataBind() GridView s.This is accomplished by calling the GridView s DataBind() method.

Как показано на рис. 9, с этими двумя обработчиками событий при возникновении одновременного нарушения отображается очень заметное сообщение.As Figure 9 shows, with these two event handlers, a very noticeable message is displayed whenever a concurrency violation occurs.

сообщение отображается на стороне одновременного нарушенияA Message is Displayed in the Face of a Concurrency Violation

Рис. 9. сообщение отображается на лицевой стороне нарушения параллелизма (щелкните, чтобы просмотреть изображение с полным размером)Figure 9: A Message is Displayed in the Face of a Concurrency Violation (Click to view full-size image)

СводкаSummary

При создании веб-приложения, в котором несколько одновременно работающих пользователей могут изменять одни и те же данные, важно учитывать параметры управления параллелизмом.When creating a web application where multiple, concurrent users may be editing the same data, it is important to consider concurrency control options. По умолчанию веб-элементы управления данными ASP.NET и элементы управления источниками данных не используют управление параллелизмом.By default, the ASP.NET data Web controls and data source controls do not employ any concurrency control. Как было показано в этом руководстве, реализация управления оптимистичным параллелизмом с помощью SqlDataSource выполняется относительно быстро и легко.As we saw in this tutorial, implementing optimistic concurrency control with the SqlDataSource is relatively quick and easy. SqlDataSource обрабатывает большую часть беготней для добавления дополнений WHERE к автоматически создаваемым UPDATE и DELETE, но существует несколько тонкостей обработки NULL столбцов значений, как описано в разделе верное Управление значениями NULL.The SqlDataSource handles most of the legwork for your adding augmented WHERE clauses to the autogenerated UPDATE and DELETE statements but there are a few subtleties in handling NULL value columns, as discussed in the Correctly Handling NULL Values section.

В этом руководстве завершается изучение SqlDataSource.This tutorial concludes our examination of the SqlDataSource. Наши оставшиеся учебники вернутся к работе с данными с помощью ObjectDataSource и многоуровневой архитектуры.Our remaining tutorials will return to working with data using the ObjectDataSource and tiered architecture.

Поздравляем с программированием!Happy Programming!

Об автореAbout the Author

Скотт Митчелл, автор семи книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями Майкрософт с 1998.Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Скотт работает как независимый консультант, преподаватель и модуль записи.Scott works as an independent consultant, trainer, and writer. Его последняя книга — Sams обучать себя ASP.NET 2,0 за 24 часа.His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Он доступен по адресу mitchell@4GuysFromRolla.com.He can be reached at mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.or via his blog, which can be found at http://ScottOnWriting.NET.