Пакетное обновление (VB)

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

Скачивание PDF

Узнайте, как обновить несколько записей базы данных в одной операции. На уровне пользовательского интерфейса мы создадим GridView, где каждая строка редактируется. В слое доступа к данным мы упаковываем несколько операций обновления в транзакцию, чтобы убедиться, что все обновления проходят успешно или откатываются.

Введение

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

В этом руководстве мы создадим GridView, где каждая строка редактируется (см. рис. 1). Так как каждая строка отрисовывается в интерфейсе редактирования, нет необходимости в столбце кнопок "Изменить", "Обновить" и "Отмена". Вместо этого на странице есть две кнопки "Обновить продукты", которые при щелчке перечисляют строки GridView и обновляют базу данных.

Каждая строка в GridView является редактируемой

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

Давайте приступим!

Примечание.

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

Изучение шагов по изменению всех строк GridView

Как описано в руководстве Обзор вставки, обновления и удаления данных, GridView предлагает встроенную поддержку редактирования базовых данных по строкам. Система GridView внутренне фиксирует, какая строка редактируется, с помощью своего EditIndex свойства. Так как GridView привязан к источнику данных, он проверяет каждую строку, чтобы узнать, равен ли индекс строки значению EditIndex. Если это так, поля этой строки отображаются с помощью интерфейсов редактирования. Для BoundFields элемент управления TextBox используется как интерфейс редактирования, и его свойству Text присваивается значение поля данных, определяемому свойством BoundField DataField. Для TemplateFields EditItemTemplate используется вместо ItemTemplate.

Помните, что рабочий процесс редактирования начинается, когда пользователь нажимает кнопку "Изменить строку". Это вызывает обратную передачу, задает свойство GridView EditIndex индексу нажатой строки и снова привязывает данные к сетке. При нажатии кнопки "Отмена" в строке, при обратной передаче EditIndex устанавливается в значение -1 перед повторной привязкой данных к таблице. Так как строки GridView начинают индексироваться с нуля, установка EditIndex в -1 приводит к отображению GridView в режиме только для чтения.

Свойство EditIndex хорошо подходит для редактирования строк, но не предназначено для пакетного редактирования. Чтобы сделать весь GridView редактируемым, необходимо, чтобы каждая строка отображалась с помощью интерфейса редактирования. Самый простой способ сделать это — создать интерфейс, где каждое редактируемое поле реализуется как TemplateField с его интерфейсом редактирования, определённым в ItemTemplate.

На следующих нескольких шагах мы создадим полностью редактируемый GridView. На шаге 1 мы начнем с создания GridView и его ObjectDataSource, а затем преобразуем связанные поля и CheckBoxField в TemplateFields. В шагах 2 и 3 мы переместим интерфейсы редактирования из шаблонных полей EditItemTemplate на их ItemTemplate .

Шаг 1. Отображение сведений о продукте

Прежде чем беспокоиться о создании GridView, где строки являются редактируемыми, начнем с того, чтобы просто отображать сведения о продукте. BatchUpdate.aspx Откройте страницу в папке BatchData и перетащите GridView из панели инструментов в конструктор. Задайте для GridView значение IDProductsGrid и из смарт-тега выберите для привязки его к новому объекту ObjectDataSource с именем ProductsDataSource. Настройте ObjectDataSource для получения данных из метода ProductsBLL класса GetProducts.

Настройка ObjectDataSource для использования класса ProductsBLL

Рис. 2. Настройка ObjectDataSource для использования ProductsBLL класса (щелкните, чтобы просмотреть изображение полного размера)

Получение данных продукта с помощью метода GetProducts

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

Как и в GridView, функции модификации ObjectDataSource предназначены для построчной работы. Чтобы обновить набор записей, необходимо написать немного кода в классе code-behind ASP.NET страницы, который группирует данные и передает их в BLL. Таким образом, задайте значения (Нет) для раскрывающихся списков на вкладках UPDATE, INSERT и DELETE объекта ObjectDataSource. Чтобы завершить работу мастера, нажмите Готово.

Задайте для списков Drop-Down на вкладках UPDATE, INSERT и DELETE значение (Нет)

Рис. 4: Установите списки Drop-Down в вкладках UPDATE, INSERT и DELETE на (Нет) (Щелкните, чтобы просмотреть изображение в полном размере)

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

<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>

Завершение работы мастера настройки источника данных также приводит к созданию BoundFields и CheckBoxField для полей данных продукта в GridView. В этом руководстве давайте разрешим пользователю просматривать и изменять имя продукта, категорию, цену и прекращенное состояние. Оставьте только поля ProductName, CategoryName, UnitPrice и Discontinued и переименуйте свойства первых трех полей в "Product", "Category" и "Price" соответственно. Наконец, установите флажки "Включить разбиение по страницам" и "Включить сортировку" в смарт-теге GridView.

На этом этапе GridView имеет три BoundFields (ProductName, CategoryNameи UnitPrice) и CheckBoxField (Discontinued). Необходимо преобразовать эти четыре поля в TemplateFields, а затем переместить интерфейс редактирования из TemplateField s EditItemTemplate в его ItemTemplate.

Примечание.

Мы изучили создание и настройку TemplateFields в руководстве по настройке интерфейса изменения данных . Мы рассмотрим шаги по преобразованию BoundFields и CheckBoxField в TemplateFields и определению их интерфейсов редактирования в своих ItemTemplate s, но если вы застряли или нуждаетесь в обновлении, не стесняйтесь обращаться к этому предыдущему руководству.

В смарт-теге GridView щелкните ссылку "Изменить столбцы", чтобы открыть диалоговое окно "Поля". Затем выберите каждое поле и нажмите кнопку "Преобразовать это поле" в ссылку TemplateField.

Преобразование существующих boundFields и CheckBoxField в TemplateFields

Рис. 5. Преобразование существующих boundFields и CheckBoxField в TemplateFields

Теперь, когда каждое поле является TemplateField, мы готовы переместить интерфейс редактирования из EditItemTemplate в ItemTemplate.

Шаг 2. Создание интерфейсовProductName, интерфейсовUnitPrice, иDiscontinued редактирование интерфейсов

ProductName, UnitPrice и Discontinued создание интерфейсов редактирования является темой этого шага и довольно просто, так как каждый интерфейс уже определен в TemplateField'ах EditItemTemplate. Создание интерфейса редактирования CategoryName представляет собой более сложный процесс, поскольку нам нужно создать DropDownList применимых категорий. Этот CategoryName интерфейс редактирования рассматривается на шаге 3.

Начнем с ProductName TemplateField. Щелкните ссылку "Изменить шаблоны" из смарт-тега GridView и выполните детализацию до ProductName TemplateField EditItemTemplate. Выберите текстовое поле, скопируйте его в буфер обмена, а затем вставьте его в ProductName файл TemplateField s ItemTemplate. Измените свойство TextBox ID на ProductName.

Затем добавьте RequiredFieldValidator ItemTemplate для обеспечения, что пользователь предоставляет значение для названия каждого продукта. Задайте для свойства ControlToValidate значение 'ProductName', укажите для свойства ErrorMessage значение 'Вы должны указать имя продукта'. и Text свойство на *. После внесения изменений в ItemTemplate, ваш экран должен выглядеть примерно так же, как на рис. 6.

TemplateField ProductName теперь включает текстовое поле и ОбязательныйПолеПроверки

Рис. 6. ПолеШаблона ProductName теперь включает TextBox и ОбязательнаяПроверкаПоля (нажмите, чтобы просмотреть изображение полного размера)

Для интерфейса редактирования UnitPrice начните с копирования текстового поля из EditItemTemplate в ItemTemplate. Затем поместите $ перед TextBox и установите для его свойства ID значение UnitPrice, а для свойства Columns — значение 8.

Кроме того, добавьте CompareValidator UnitPrice в s ItemTemplate , чтобы убедиться, что значение, введенное пользователем, является допустимым значением валюты больше или равно $0,00. Задайте свойству проверяющего элемента ControlToValidate значение UnitPrice, а его свойству ErrorMessage значение "Вы должны ввести допустимое значение валюты". Пожалуйста, удалите любые символы валюты, установите его свойство Text на *, установите его свойство Type на Currency, установите его свойство Operator на GreaterThanEqual, и установите его свойство ValueToCompare на 0.

Добавьте элемент CompareValidator, чтобы убедиться, что введенная цена является неотрицательным денежным значением

Рис. 7. Добавьте CompareValidator, чтобы убедиться, что введённая цена является неотрицательной денежной величиной (Нажмите, чтобы просмотреть изображение полного размера)

Discontinued Для TemplateField можно использовать флажок, уже определенный в файле ItemTemplate. Просто установите значение ID как "Прекращено" и задайте его свойство Enabled на True.

Шаг 3. СозданиеCategoryNameинтерфейса редактирования

Интерфейс редактирования в CategoryName TemplateField содержит текстовое поле EditItemTemplate, отображающее значение поля данных CategoryName. Нам нужно заменить это на выпадающий список, который перечисляет возможные категории.

Примечание.

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

Перетащите выпадающий список из панели инструментов в CategoryName ШаблонФилд ItemTemplate, задав его значение IDCategories. На этом этапе мы обычно определим источник данных DropDownLists с помощью смарт-тега, создав объект ObjectDataSource. Однако это добавит ObjectDataSource внутри ItemTemplate, что приведет к созданию экземпляра ObjectDataSource для каждой строки GridView. Вместо этого давайте создадим ObjectDataSource вне полей шаблона GridView. Завершите редактирование шаблона, перетащите ObjectDataSource из панели элементов в конструктор под ProductsDataSource ObjectDataSource. Назовите новый ObjectDataSource CategoriesDataSource и настройте его для использования метода класса CategoriesBLL с GetCategories.

Настройка ObjectDataSource для использования класса CategoriesBLL

Рис. 8. Настройка ObjectDataSource для использования CategoriesBLL класса (щелкните, чтобы просмотреть изображение полного размера)

Получение данных категории с помощью метода GetCategories

Рис. 9. Получение данных категории с помощью GetCategories метода (щелкните, чтобы просмотреть изображение полного размера)

Поскольку ObjectDataSource используется только для извлечения данных, установите раскрывающиеся списки на вкладках UPDATE и DELETE в (Нет). Чтобы завершить работу мастера, нажмите Готово.

Задайте значение (Нет) для списков Drop-Down на вкладках UPDATE и DELETE.

Рис. 10. Установите списки Drop-Down на вкладках UPDATE и DELETE в положение (Отсутствует) (Щелкните для просмотра изображения в полном размере)

После завершения работы мастера декларативная разметка CategoriesDataSource должна выглядеть следующим образом:

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

CategoriesDataSource После создания и настройки вернитесь в CategoryName TemplateField ItemTemplate и в смарт-теге DropDownList щелкните ссылку "Выбор источника данных". В мастере настройки источника данных выберите CategoriesDataSource параметр из первого раскрывающегося списка и выберите CategoryName для отображения и CategoryID в качестве значения.

Привяжите выпадающий список к CategoriesDataSource

Рис. 11: Привяжите раскрывающийся список к CategoriesDataSource (щелкните, чтобы просмотреть изображение полного размера)

На этом этапе раскрывающийся список Categories перечисляет все категории, но пока не выбирает соответствующую категорию для товара, привязанного к строке GridView. Для этого необходимо установить значение Categories DropDownList на значение продукта CategoryID. Щелкните ссылку "Изменить DataBindings" на смарт-теге DropDownList и свяжите SelectedValue свойство с полем CategoryID данных, как показано на рис. 12.

Привяжите значение CategoryID товара к свойству SelectedValue элемента управления DropDownList

Рис. 12. Привязка значения продукта CategoryID к свойству DropDownList SelectedValue

Остается одна последняя проблема: если для продукта не указано значение CategoryID, то выражение привязки данных на SelectedValue приведет к исключению. Это связано с тем, что DropDownList содержит только элементы для категорий и не предлагает вариант для тех продуктов, которые имеют значение NULL в базе данных для CategoryID. Чтобы устранить эту проблему, задайте свойство AppendDataBoundItems для DropDownList и добавьте новый элемент в DropDownList, опустив свойство Value из декларативного синтаксиса. То есть убедитесь, что Categories декларативный синтаксис DropDownList выглядит следующим образом:

<asp:DropDownList ID="Categories" runat="server" AppendDataBoundItems="True" 
    DataSourceID="CategoriesDataSource" DataTextField="CategoryName" 
    DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
    <asp:ListItem Value=">-- Select One --</asp:ListItem>
</asp:DropDownList>

Обратите внимание, что <asp:ListItem Value=""> -- Select One -- имеет свой атрибут Value, явно установленный в пустую строку. Вернитесь к директиве по настройке интерфейса изменения данных, чтобы более подробно обсудить, почему этот дополнительный элемент DropDownList необходим, чтобы иметь возможность обработать NULL случай и почему назначение свойству Value пустой строки является важным.

Примечание.

Здесь существует потенциальная проблема с производительностью и масштабируемостью, которую стоит упомянуть. Поскольку каждая строка имеет DropDownList, который использует CategoriesDataSource в качестве источника данных, метод класса CategoriesBLLGetCategories будет вызываться n раз при каждом посещении страницы, где n — это количество строк в GridView. Эти n вызовов GetCategories приводят к n запросам к базе данных. Это влияние на базу данных может быть уменьшено путем кэширования возвращаемых категорий в кэше каждого запроса или через уровень кэширования с помощью зависимости кэширования SQL или с использованием очень короткого срока действия, основанного на времени.

Шаг 4. Завершение интерфейса редактирования

Мы внесли ряд изменений в шаблоны GridView без приостановки просмотра хода выполнения. Ознакомьтесь с нашим прогрессом в браузере. Как показано на рисунке 13, каждая строка отрисовывается с помощью своего ItemTemplate, который содержит интерфейс редактирования ячеек.

Каждая строка GridView редактируется

Рис. 13. Каждая строка GridView редактируется (щелкните, чтобы просмотреть изображение полного размера)

На этом этапе мы должны разобраться с несколькими незначительными проблемами форматирования. Во-первых, обратите внимание, что UnitPrice значение содержит четыре десятичных знака. Чтобы устранить эту проблему, вернитесь к UnitPrice TemplateField ItemTemplate, и из меню смарт-тага TextBox щелкните на ссылке "Изменить привязки данных". Затем укажите, что Text свойство должно быть отформатировано как число.

Форматирование текстового свойства в виде числа

Рис. 14. Форматирование Text свойства в виде числа

Во-вторых, расположим флажок по центру в Discontinued столбце (вместо выравнивания по левому краю). Щелкните "Изменить столбцы" в смарт-теге GridView и выберите Discontinued TemplateField из списка полей в левом нижнем углу. Перейдите в ItemStyle, и установите свойство HorizontalAlign в положение Центр, как показано на рис. 15.

Разместить флажок

Рис. 15. Центрируйте Discontinued флажок

Затем добавьте элемент управления ValidationSummary на страницу и задайте его свойству ShowMessageBox значение True, а свойству ShowSummary значение False. Кроме того, добавьте веб-элементы управления "Кнопка", которые при нажатии будут обновлять изменения пользователя. В частности, добавьте два веб-элемента управления Button, один над GridView и один под ним, установив для обоих элементов управления свойство Text с именем "Update Products".

Так как интерфейс редактирования GridView определен в его TemplateFields ItemTemplate , EditItemTemplate они являются лишними и могут быть удалены.

После внесения указанных выше изменений форматирования, добавления управляющих элементов Button и удаления ненужных EditItemTemplate, синтаксис вашей страницы должен выглядеть следующим образом:

<p>
    <asp:Button ID="UpdateAllProducts1" runat="server" Text="Update Products" />
</p>
<p>
    <asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
        AllowPaging="True" AllowSorting="True">
        <Columns>
            <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
                <ItemTemplate>
                    <asp:TextBox ID="ProductName" runat="server" 
                        Text='<%# Bind("ProductName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                        ControlToValidate="ProductName"
                        ErrorMessage="You must provide the product's name." 
                        runat="server">*</asp:RequiredFieldValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Category" 
                SortExpression="CategoryName">
                <ItemTemplate>
                    <asp:DropDownList ID="Categories" runat="server" 
                        AppendDataBoundItems="True" 
                        DataSourceID="CategoriesDataSource"
                        DataTextField="CategoryName" 
                        DataValueField="CategoryID" 
                        SelectedValue='<%# Bind("CategoryID") %>'>
                        <asp:ListItem>-- Select One --</asp:ListItem>
                    </asp:DropDownList>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Price" 
                SortExpression="UnitPrice">
                <ItemTemplate>
                    $<asp:TextBox ID="UnitPrice" runat="server" Columns="8" 
                        Text='<%# Bind("UnitPrice", "{0:N}") %>'></asp:TextBox>
                    <asp:CompareValidator ID="CompareValidator1" runat="server" 
                        ControlToValidate="UnitPrice"
                        ErrorMessage="You must enter a valid currency value. 
                                      Please omit any currency symbols."
                        Operator="GreaterThanEqual" Type="Currency" 
                        ValueToCompare="0">*</asp:CompareValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
                <ItemTemplate>
                    <asp:CheckBox ID="Discontinued" runat="server" 
                        Checked='<%# Bind("Discontinued") %>' />
                </ItemTemplate>
                <ItemStyle HorizontalAlign="Center" />
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
</p>
<p>
    <asp:Button ID="UpdateAllProducts2" runat="server" Text="Update Products" />
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
    <asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetCategories" TypeName="CategoriesBLL">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="ValidationSummary1" runat="server" 
        ShowMessageBox="True" ShowSummary="False" />
</p>

На рисунке 16 показана эта страница при просмотре через браузер после добавления веб-элементов управления типа Button и изменения форматирования.

Страница теперь включает две кнопки обновления продуктов

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

Шаг 5. Обновление продуктов

Когда пользователь посещает эту страницу, он будет вносить изменения и нажимать одну из двух кнопок "Обновить продукты". На этом этапе необходимо как-то сохранить введенные пользователем значения для каждой ProductsDataTable строки в экземпляр, а затем передать его в метод BLL, который затем передает этот ProductsDataTable экземпляр методу DAL UpdateWithTransaction . Метод UpdateWithTransaction , созданный в предыдущем руководстве, гарантирует, что пакет изменений будет обновлен как атомарная операция.

Создайте метод с именем BatchUpdate в BatchUpdate.aspx.vb, затем добавьте следующий код:

Private Sub BatchUpdate()
    ' Enumerate the GridView's Rows collection and create a ProductRow
    Dim productsAPI As New ProductsBLL()
    Dim products As Northwind.ProductsDataTable = productsAPI.GetProducts()
    For Each gvRow As GridViewRow In ProductsGrid.Rows
        ' Find the ProductsRow instance in products that maps to gvRow
        Dim productID As Integer = _
            Convert.ToInt32(ProductsGrid.DataKeys(gvRow.RowIndex).Value)
        Dim product As Northwind.ProductsRow = products.FindByProductID(productID)
        If product IsNot Nothing Then
            ' Programmatically access the form field elements in the 
            ' current GridViewRow
            Dim productName As TextBox = _
                CType(gvRow.FindControl("ProductName"), TextBox)
            Dim categories As DropDownList = _
                CType(gvRow.FindControl("Categories"), DropDownList)
            Dim unitPrice As TextBox = _
                CType(gvRow.FindControl("UnitPrice"), TextBox)
            Dim discontinued As CheckBox = _
                CType(gvRow.FindControl("Discontinued"), CheckBox)
            ' Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim()
            If categories.SelectedIndex = 0 Then 
                product.SetCategoryIDNull() 
            Else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue)
            End If
            If unitPrice.Text.Trim().Length = 0 Then 
                product.SetUnitPriceNull() 
            Else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text)
            End If
            product.Discontinued = discontinued.Checked
        End If
    Next
    ' Now have the BLL update the products data using a transaction
    productsAPI.UpdateWithTransaction(products)
End Sub

Этот метод начинается с получения всех продуктов обратно через вызов метода ProductsDataTable в BLL GetProducts. Затем он перечисляет коллекцию ProductGrid GridViewRows. Коллекция Rows содержит экземпляр для каждой GridViewRow строки, отображаемой в GridView. Так как мы отображаем не более десяти строк на страницу, коллекция GridView Rows будет иметь не более десяти элементов.

Для каждой строки ProductID извлекается из коллекции DataKeys и выбирается соответствующий ProductsRow из ProductsDataTable. Четыре элемента управления входными данными TemplateField программно обращаются, и их значения назначаются свойствам экземпляра ProductsRow. После того как значения строк GridView были использованы для обновления ProductsDataTable, он передается методу BLL UpdateWithTransaction, который, как мы видели в предыдущем руководстве, просто вызывает метод DAL UpdateWithTransaction.

Алгоритм пакетного обновления, используемый для этого руководства, обновляет каждую строку в ProductsDataTable, которая соответствует строке в GridView, вне зависимости от изменения сведений о продукте. Хотя такие слепые обновления обычно не являются проблемой производительности, они могут привести к лишним записям, если вы выполняете аудит изменений в таблице базы данных. В руководстве по выполнению пакетных обновлений мы изучили интерфейс пакетного обновления с помощью DataList и добавили код, который будет обновлять только те записи, которые фактически были изменены пользователем. При необходимости вы можете использовать методы выполнения пакетных обновлений для обновления кода в этом руководстве.

Примечание.

При привязке источника данных к GridView с помощью смарт-тега Visual Studio автоматически назначает значения первичного ключа источника данных свойству GridView DataKeyNames . Если объект ObjectDataSource не привязывается к GridView с помощью смарт-тега GridView, как описано на шаге 1, необходимо вручную задать для свойства GridView DataKeyNames значение ProductID, чтобы получить доступ к ProductID значению каждой строки через DataKeys коллекцию.

Код, используемый в BatchUpdate, аналогичен тому, который используется в методах UpdateProduct BLL, основное отличие заключается в том, что в методах UpdateProduct извлекается только один экземпляр ProductRow из архитектуры. Код, назначающий свойства объекта ProductRow, такой же, как и у методов UpdateProducts и кода в цикле For Each в BatchUpdate, так же, как и общий шаблон.

Чтобы завершить работу с этим руководством, необходимо вызвать метод BatchUpdate при нажатии любой из кнопок 'Update Products'. Создайте обработчики для событий двух указанных кнопок Click и добавьте следующий код в обработчики событий:

BatchUpdate()
ClientScript.RegisterStartupScript(Me.GetType(), "message", _
    "alert('The products have been updated.');", True)

Сначала вызов выполняется BatchUpdate. ClientScript Затем свойство используется для внедрения JavaScript, который отображает сообщение: Продукты были обновлены.

Минуту, чтобы проверить этот код. Посетите BatchUpdate.aspx через браузер, измените несколько строк и нажмите одну из кнопок "Обновить данные товаров". Если ошибки проверки входных данных отсутствуют, вы увидите сообщение с текстом: "Продукты были обновлены". Чтобы проверить атомарность обновления, попробуйте добавить произвольное CHECK ограничение, например, которое запрещает UnitPrice значение 1234,56. BatchUpdate.aspxЗатем отредактируйте ряд записей, убедившись, что одно из значений продукта UnitPrice имеет запрещенное значение (1234,56). Это приведет к ошибке при нажатии кнопки «Обновить продукты», и другие изменения во время этой пакетной операции будут отменены до исходных значений.

АльтернативныйBatchUpdateметод

Метод BatchUpdate , который мы только что изучили, извлекает все продукты из метода BLL GetProducts , а затем обновляет только те записи, которые отображаются в GridView. Этот подход идеально подходит в случае, если GridView не использует разбиение на страницы. Однако если все же используется, количество продуктов может достигать сотен, тысяч или даже десятков тысяч, в то время как в GridView будет отображаться только десять строк. В таком случае получение всех продуктов из базы данных для изменения только 10 из них не является оптимальным.

Для таких типов ситуаций рекомендуется использовать следующий BatchUpdateAlternate метод:

Private Sub BatchUpdateAlternate()
    ' Enumerate the GridView's Rows collection and create a ProductRow
    Dim productsAPI As New ProductsBLL()
    Dim products As New Northwind.ProductsDataTable()
    For Each gvRow As GridViewRow In ProductsGrid.Rows
        ' Create a new ProductRow instance
        Dim productID As Integer = _
            Convert.ToInt32(ProductsGrid.DataKeys(gvRow.RowIndex).Value)
        Dim currentProductDataTable As Northwind.ProductsDataTable = _
            productsAPI.GetProductByProductID(productID)
        If currentProductDataTable.Rows.Count > 0 Then
            Dim product As Northwind.ProductsRow = currentProductDataTable(0)
            Dim productName As TextBox = _
                CType(gvRow.FindControl("ProductName"), TextBox)
            Dim categories As DropDownList = _
                CType(gvRow.FindControl("Categories"), DropDownList)
            Dim unitPrice As TextBox = _
                CType(gvRow.FindControl("UnitPrice"), TextBox)
            Dim discontinued As CheckBox = _
                CType(gvRow.FindControl("Discontinued"), CheckBox)
            ' Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim()
            If categories.SelectedIndex = 0 Then 
                product.SetCategoryIDNull() 
            Else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue)
            End If
            If unitPrice.Text.Trim().Length = 0 Then 
                product.SetUnitPriceNull() 
            Else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text)
            End If
            product.Discontinued = discontinued.Checked
            ' Import the ProductRow into the products DataTable
            products.ImportRow(product)
        End If
    Next
    ' Now have the BLL update the products data using a transaction
    productsAPI.UpdateProductsWithTransaction(products)
End Sub

BatchMethodAlternate начинается с создания нового пустого ProductsDataTable с именем products. Затем он пошагово проходит по коллекции GridView Rows и методом BLL GetProductByProductID(productID) получает определенную информацию о продукте для каждой строки. Полученный экземпляр обновляет свои свойства таким же образом, как , но после обновления строки он импортируется в с помощью метода DataTable.

После завершения For Each цикла содержит один products экземпляр для каждой ProductsRow строки в GridView. Так как каждый из экземпляров ProductsRow был добавлен в products (вместо обновления), если мы бездумно передадим его методу UpdateWithTransaction, ProductsTableAdapter попытается вставить каждую из записей в базу данных. Вместо этого необходимо указать, что каждая из этих строк была изменена (не добавлена).

Это можно сделать, добавив новый метод в BLL с именем UpdateProductsWithTransaction. UpdateProductsWithTransaction, как показано ниже, устанавливает Modified для каждого из экземпляров ProductsRow в списке ProductsDataTable, а затем передает ProductsDataTable в метод UpdateWithTransaction DAL.

Public Function UpdateProductsWithTransaction _
    (ByVal products As Northwind.ProductsDataTable) As Integer
    ' Mark each product as Modified
    products.AcceptChanges()
    For Each product As Northwind.ProductsRow In products
        product.SetModified()
    Next
    ' Update the data via a transaction
    Return UpdateWithTransaction(products)
End Function

Итоги

GridView предоставляет встроенные возможности редактирования строк, но не поддерживает создание полностью редактируемых интерфейсов. Как мы видели в этом руководстве, такие интерфейсы возможны, но требуют немного работы. Чтобы создать GridView, где каждая строка редактируется, необходимо преобразовать поля GridView в TemplateFields и определить интерфейс редактирования в пределах ItemTemplate S. Кроме того, необходимо добавить на страницу веб-элементы управления кнопками типа 'Обновить всё' вне компонента GridView. Эти обработчики событий кнопок Click должны перечислить коллекцию элементов GridView Rows, сохранить изменения в объекте ProductsDataTable, и передать обновленную информацию в соответствующий метод BLL.

В следующем руководстве мы посмотрим, как создать интерфейс для пакетного удаления. В частности, каждая строка GridView будет содержать флажок, и вместо кнопок типа "Обновить все" будут использоваться кнопки "Удалить выбранные строки".

Счастливое программирование!

Об авторе

Скотт Митчелл, автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Изучаем ASP.NET 2.0 за 24 часа с помощью Sams. Его можно достичь по адресу mitchell@4GuysFromRolla.com.

Особое спасибо кому

Эта серия учебников была проверена многими полезными рецензентами. Ведущие рецензенты этого руководства — Тереса Мерфи и Дэвид Суру. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com.