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

по Скотт Митчелл

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

В этом учебнике мы рассмотрим основные принципы управления оптимистичным параллелизмом, а затем рассмотрим, как его реализовать с помощью элемента управления SqlDataSource.

Введение

В предыдущем учебном курсе мы рассмотрели, как добавлять в элемент управления SqlDataSource функции вставки, обновления и удаления. Вкратце, чтобы предоставить эти функции, необходимые для указания соответствующего INSERT, UPDATEили DELETE инструкции SQL в свойствах InsertCommand, UpdateCommandили DeleteCommand, а также соответствующих параметров в коллекциях InsertParameters, UpdateParametersи DeleteParameters. Хотя эти свойства и коллекции можно указать вручную, кнопка "настроить мастер источников данных" дополнительно предлагает флажок "создать INSERT, UPDATEи DELETE инструкции", который автоматически создаст эти инструкции на основе инструкции SELECT.

Вместе с флажком создать INSERT, UPDATEи DELETE в диалоговом окне Дополнительные параметры создания SQL включено использование оптимистичного параллелизма (см. рис. 1). Если этот флажок установлен, WHERE предложения в автоматически сформированных инструкциях UPDATE и DELETE изменяются для выполнения обновления или удаления только в том случае, если данные базовой базы данных не были изменены с момента последней загрузки данных пользователем в сетке.

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

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

В учебнике реализация оптимистичного параллелизма мы рассмотрели основные принципы управления оптимистичным параллелизмом и как добавить его в ObjectDataSource. В этом учебнике мы воспользуемся основными принципами управления оптимистичным параллелизмом, а затем рассмотрим, как его реализовать с помощью SqlDataSource.

Обзор оптимистичного параллелизма

Для веб-приложений, позволяющих нескольким пользователям одновременно изменять или удалять одни и те же данные, существует вероятность, что один пользователь может случайно перезаписать другие изменения. В учебнике реализация оптимистичного параллелизма я предоставил следующий пример:

Предположим, что два пользователя, Цзисунь и SAM, посещали страницу в приложении, которое позволяло посетителям обновлять и удалять продукты с помощью элемента управления GridView. Одновременно нажмите кнопку изменить для Chai. Цзисунь изменяет название продукта на чай Chai и нажимает кнопку Обновить. Результатом является UPDATEная инструкция, которая отправляется в базу данных, которая задает все обновляемые поля продукта (несмотря на то, что цзисунь только обновил одно поле, ProductName). На данный момент база данных имеет значения Чай Chai, Категория напитки, поставщик Exotic Liquids и т. д. для этого конкретного продукта. Однако на экране GridView on SAM по-прежнему отображается название продукта в редактируемой строке GridView в виде Chai. Через несколько секунд после того, как изменения Цзисунь s зафиксированы, Сэм обновляет категорию на «специи» и нажимает кнопку Обновить. В результате инструкция UPDATE отправляется в базу данных, которая задает имя продукта Chai, CategoryID с соответствующим ИДЕНТИФИКАТОРом категории «специи» и т. д. Изменения, внесенные в цзисунь с названием продукта, были перезаписаны.

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

, когда два пользователя одновременно обновляют запись a, существует вероятность того, что один пользователь s может перезаписать другие

Рис. 2. когда два пользователя одновременно обновляют запись a, существует вероятность того, что один пользователь s может перезаписать другие (щелкните, чтобы просмотреть изображение с полным размером).

Чтобы предотвратить несворачивание этого сценария, необходимо реализовать форму управления параллелизмом . Оптимистическая блокировка . в этом учебнике рассматривается предположение о том, что при возникновении конфликтов параллелизма, а затем подавляющее большинство времени таких конфликтов не возникает. Таким образом, если возникает конфликт, управление оптимистичным параллелизмом просто информирует пользователя о том, что их изменения можно сохранить, так как другой пользователь изменил те же данные.

Note

Для приложений, в которых предполагается наличие множества конфликтов параллелизма или если такие конфликты не приемлемой, вместо этого можно использовать Пессимистическое управление параллелизмом. Дополнительные сведения об управлении пессимистичным параллелизмом см. в статье реализация оптимистичного параллелизма в учебнике.

Управление оптимистичным параллелизмом работает, гарантируя, что обновляемая или Удаляемая запись имеет те же значения, что и при запуске процесса обновления или удаления. Например, при нажатии кнопки изменить в редактируемом элементе GridView значения записей считываются из базы данных и отображаются в текстовых полях и других веб-элементах управления. Эти исходные значения сохраняются в GridView. Позже, после того, как пользователь вносит изменения и нажимает кнопку "Обновить", в используемой инструкции UPDATE необходимо учитывать исходные значения плюс новые значения, а также обновлять базовую запись базы данных только в том случае, если исходные значения, которые пользователь запустил редактирование, идентичны значениям в базе данных. На рис. 3 показана эта последовательность событий.

для успешности обновления или удаления исходные значения должны быть равны текущим значениям базы данных.

Рис. 3. для успешности обновления или удаления исходные значения должны быть равны текущим значениям базы данных (щелкните, чтобы просмотреть изображение с полным размером).

Существует множество подходов к реализации оптимистичного параллелизма (см. Питер. статье логику обновления оптимистичного параллелизма для краткого взгляда на ряд параметров). Методика, используемая SqlDataSource (а также ADO.NET типизированными наборами данных, используемыми в нашем уровне доступа к данным) дополняет предложение WHERE для включения сравнения всех исходных значений. Следующая инструкция 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

Как мы увидим в этом руководстве, включение управления оптимистичным параллелизмом с помощью SqlDataSource — это просто проверка флажка.

Шаг 1. Создание SqlDataSource, поддерживающего оптимистичный параллелизм

Для начала откройте страницу OptimisticConcurrency.aspx из папки SqlDataSource. Перетащите элемент управления SqlDataSource из панели элементов в конструктор, заID его свойство для ProductsDataSourceWithOptimisticConcurrency. Затем щелкните ссылку Configure Data Source (настроить источник данных) в смарт-теге элемента управления. На первом экране мастера выберите работа с NORTHWINDConnectionString и нажмите кнопку Далее.

выбрать работу с NORTHWINDConnectionString

Рис. 4. Выбор режима работы с NORTHWINDConnectionString (щелкните, чтобы просмотреть изображение с полным размером)

В этом примере мы добавим элемент управления GridView, который позволяет пользователям редактировать таблицу Products. Поэтому на экране Настройка инструкции Select выберите таблицу Products из раскрывающегося списка и выберите столбцы ProductID, ProductName, UnitPriceи Discontinued, как показано на рис. 5.

из таблицы Products возвращает столбцы ProductID, ProductName, UnitPrice и неподдерживаемые

Рис. 5. из таблицы Products возвратить столбцы ProductID, ProductName, UnitPriceи Discontinued (щелкните,чтобы просмотреть изображение с полным размером)

После выбора столбцов нажмите кнопку Дополнительно, чтобы открыть диалоговое окно Дополнительные параметры создания SQL. Установите флажки создать INSERT, UPDATEи DELETE и использовать флажки оптимистичного параллелизма и нажмите кнопку ОК (см. рис. 1 на снимке экрана). Завершите работу мастера, нажав кнопку Далее, а затем Готово.

После завершения работы мастера настройки источника данных изучите результирующие DeleteCommand и UpdateCommand свойства и коллекции DeleteParameters и UpdateParameters. Самый простой способ сделать это — щелкнуть вкладку Источник в левом нижнем углу, чтобы увидеть декларативный синтаксис страницы. Здесь вы увидите 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

С семью параметрами в коллекции UpdateParameters:

<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 должны выглядеть следующим образом:

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 (и добавления дополнительных параметров в соответствующие коллекции параметров), выбор параметра использовать оптимистичный параллелизм корректирует два других свойства:

Когда веб-элемент управления данными вызывает метод SqlDataSource Update() или Delete(), он передает исходные значения. Если свойство ConflictDetection SqlDataSource s имеет значение CompareAllValues, эти исходные значения добавляются в команду. Свойство OldValuesParameterFormatString предоставляет шаблон именования, используемый для исходных параметров значения. Мастер настройки источников данных использует исходный_{0} и называет каждый исходный параметр в свойствах UpdateCommand и DeleteCommand и соответственно UpdateParameters и DeleteParameters коллекций.

Note

Так как мы не используем возможности вставки элементов управления SqlDataSource, вы можете удалить свойство InsertCommand и его InsertParametersную коллекцию.

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

К сожалению, дополненные UPDATE и DELETE инструкции, автоматически созданные мастером настройки источника данных при использовании оптимистичного параллелизма, не работают с записями, содержащими значения NULL. Чтобы узнать, почему, рассмотрим наш UpdateCommandSqlDataSource:

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 значения. Если определенная запись имеет NULL значение для UnitPrice, то WHERE предложение [UnitPrice] = @original_UnitPrice всегда будет иметь значение false, так как NULL = NULL всегда возвращает значение false. Поэтому записи, содержащие NULL значения, не могут быть изменены или удалены, так как операторы UPDATE и DELETE WHERE предложения не возвращают строк для обновления или удаления.

Note

Эта ошибка впервые была передана в корпорацию Майкрософт в июне 2004 в SqlDataSource создает неверные инструкции SQL и сообщает о том, что в следующей версии ASP.NET запланировано исправление отчета.

Чтобы устранить эту проблему, необходимо вручную обновить предложения WHERE в свойствах UpdateCommand и DeleteCommand для всех столбцов, которые могут иметь NULL значения. В общем случае измените [ColumnName] = @original_ColumnName на:

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

Это изменение можно выполнить непосредственно через декларативную разметку с помощью параметров Упдатекуери или Делетекуери окно свойств или на вкладках "Обновить" и "Удалить" в параметре "указать пользовательскую инструкцию SQL или хранимую процедуру" в разделе "Настройка данных". Мастер источников. Опять же, это изменение необходимо выполнить для каждого столбца в предложении UpdateCommand и DeleteCommand s WHERE, который может содержать значения NULL.

Применение этого примера к нашему примеру приводит к следующим измененным UpdateCommand и DeleteCommandным значениям:

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 с параметрами правки и удаления

При настройке SqlDataSource для поддержки оптимистичного параллелизма остается только добавить веб-элемент управления данными на страницу, использующую этот элемент управления параллелизмом. В этом руководстве мы добавим GridView, который предоставляет функции редактирования и удаления. Для этого перетащите элемент GridView с панели элементов в конструктор и задайте для его ID значение Products. В смарт-теге GridView s привяжите его к элементу управления ProductsDataSourceWithOptimisticConcurrency SqlDataSource, добавленному на шаге 1. Наконец, установите флажки Включить редактирование и включить удаление из смарт-тега.

привязать GridView к SqlDataSource и разрешить редактирование и удаление

Рис. 6. привязка элемента управления GridView к SqlDataSource и включение редактирования и удаления (щелкните, чтобы просмотреть изображение с полным размером)

После добавления GridView настройте его внешний вид, удалив ProductID BoundField, изменив свойство ProductName BoundField HeaderText на Product и обновив BoundField UnitPrice, чтобы его свойство HeaderText было просто ценой. В идеале мы улучшаем интерфейс правки, чтобы включить RequiredFieldValidator для значения ProductName и CompareValidator для UnitPriceного значения (чтобы убедиться, что оно имеет правильно отформатированное числовое значение). Более подробные сведения о настройке интерфейса редактирования GridView s см. в руководстве по настройке интерфейса изменения данных .

Note

Состояние представления GridView s должно быть включено, так как исходные значения, переданные из GridView в SqlDataSource, хранятся в состоянии представления.

После внесения этих изменений в GridView декларативная разметка GridView и SqlDataSource должна выглядеть следующим образом:

<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. Нажмите кнопки "Изменить" для первого продукта в обоих браузерах. В одном браузере измените название продукта и нажмите кнопку Обновить. Браузер выполнит обратную передачу, и GridView вернется в режим предварительного редактирования, отображая новое название продукта для только что измененной записи.

Во втором окне браузера измените цену (но оставьте имя продукта исходным значением) и нажмите кнопку Обновить. При обратной передаче сетка возвращается в режим предварительного редактирования, но изменение цены не записывается. Второй браузер отображает то же самое значение, что и первое название продукта со старой ценой. Изменения, внесенные во втором окне браузера, были потеряны. Более того, изменения были потеряны, так как не существовало исключений или сообщений, указывающих на возникновение одновременного нарушения.

изменения во втором окне браузера были автоматически потеряны

Рис. 7. изменения во втором окне браузера были автоматически потеряны (щелкните, чтобы просмотреть изображение с полным размером)

Причина, по которой изменения во втором браузере не были зафиксированы, заключается в том, что предложение UPDATE инструкции s 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

Когда второе окно браузера обновляет запись, исходное название продукта, указанное в предложении WHERE, не будет соответствовать существующему названию продукта (поскольку оно было изменено первым браузером). Таким образом, инструкция [ProductName] = @original_ProductName возвращает значение false, а UPDATE не влияет на какие бы то ни было записи.

Note

Функция удаления работает аналогичным образом. Откройте два окна браузера, начните с изменения определенного продукта, а затем сохранив его изменения. После сохранения изменений в одном браузере нажмите кнопку Удалить для того же продукта в другом. Поскольку исходные значения не соответствуют t в предложении WHERE оператора DELETE, удаление не выполняется автоматически.

В перспективе "конечные пользователи" во втором окне браузера после нажатия кнопки "Обновить" Сетка возвращается в режим предварительного редактирования, но изменения были потеряны. Однако нет визуальных отзывов о том, что их изменения не были внесены. В идеале, если изменения пользователя теряются до нарушения параллелизма, мы будем уведомлять их и, возможно, сохраняют сетку в режиме редактирования. Давайте посмотрим, как это сделать.

Шаг 3. Определение времени возникновения одновременного нарушения

Так как нарушение параллелизма отклоняет сделанные изменения, было бы неплохо оповещать пользователя при возникновении одновременного нарушения. Чтобы предупредить пользователя, добавим элемент управления Label в верхнюю часть страницы с именем ConcurrencyViolationMessage, свойство Text которого отображает следующее сообщение: предпринята попытка обновить или удалить запись, которая одновременно обновлялась другим пользователем. Проверьте изменения другого пользователя, а затем повторите обновление или удаление. Задайте для свойства Метка CssClass свойство значение предупреждение, которое является классом CSS, определенным в Styles.css, который отображает текст красным, курсивным, полужирным и крупным шрифтом. Наконец, задайте метку s Visible и свойства EnableViewState для False. Это приведет к скрытию метки только для тех обратных передач, в которых для свойства Visible задано значение True.

добавить на страницу элемент управления Label для вывода предупреждения

Рис. 8. Добавление на страницу элемента управления Label для отображения предупреждения (щелкните, чтобы просмотреть изображение с полным размером)

При выполнении Update или DELETE обработчики событий GridView RowUpdated и RowDeleted срабатывают после того, как элемент управления источником данных выполнил запрошенное обновление или удалить. Мы можем определить, сколько строк было затронуто операцией из этих обработчиков событий. Если затронутые нулевые строки, необходимо отобразить ConcurrencyViolationMessage метку.

Создайте обработчик событий для событий RowUpdated и RowDeleted и добавьте следующий код:

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. В обработчике событий RowUpdated мы также предложите элементу управления GridView оставаться в режиме редактирования, установив для свойства KeepInEditMode значение true. При этом необходимо повторно привязать данные к сетке, чтобы другие пользовательские данные загружались в интерфейс редактирования. Это достигается путем вызова метода DataBind() GridView s.

Как показано на рис. 9, с этими двумя обработчиками событий при возникновении одновременного нарушения отображается очень заметное сообщение.

сообщение отображается на стороне одновременного нарушения

Рис. 9. сообщение отображается на лицевой стороне нарушения параллелизма (щелкните, чтобы просмотреть изображение с полным размером)

Сводка

При создании веб-приложения, в котором несколько одновременно работающих пользователей могут изменять одни и те же данные, важно учитывать параметры управления параллелизмом. По умолчанию веб-элементы управления данными ASP.NET и элементы управления источниками данных не используют управление параллелизмом. Как было показано в этом руководстве, реализация управления оптимистичным параллелизмом с помощью SqlDataSource выполняется относительно быстро и легко. SqlDataSource обрабатывает большую часть беготней для добавления дополнений WHERE к автоматически создаваемым UPDATE и DELETE, но существует несколько тонкостей обработки NULL столбцов значений, как описано в разделе верное Управление значениями NULL.

В этом руководстве завершается изучение SqlDataSource. Наши оставшиеся учебники вернутся к работе с данными с помощью ObjectDataSource и многоуровневой архитектуры.

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

Об авторе

Скотт Митчелл, автор семи книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями Майкрософт с 1998. Скотт работает как независимый консультант, преподаватель и модуль записи. Его последняя книга — Sams обучать себя ASP.NET 2,0 за 24 часа. Он доступен по адресу mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.