Поделиться через


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

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

Загрузить PDF-файл

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

Введение

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

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

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

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

Приступим!

Примечание

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

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

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

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

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

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

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

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

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

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

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

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

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

Задайте для 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>

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

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

Примечание

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

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

Преобразование существующих полей BoundField и CheckBoxField в templateField

Рис. 5. Преобразование существующих полей BoundField и CheckBoxField в templateField

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

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

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

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

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

ProductName TemplateField теперь включает TextBox и RequiredFieldValidator.

Рис. 6. TemplateField ProductName Now includes a TextBox and a RequiredFieldValidator (Щелкните для просмотра полноразмерного изображения)

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

Кроме того, добавьте CompareValidator в UnitPrice объект , ItemTemplate чтобы убедиться, что введенное пользователем значение является допустимым значением в валюте, превышающим или равным 0,00 долл. США. Задайте для свойства проверяющего ControlToValidate элемента управления значение UnitPrice, а для его ErrorMessage свойства — значение Необходимо ввести допустимое значение валюты. Опустите все символы валюты, свойство — Text *, Type свойство CurrencyOperator — , свойство — GreaterThanEqual, а свойство — значение , а свойство ValueToCompare — значение 0.

Добавьте CompareValidator, чтобы убедиться, что введенная цена является неотрицательной валютой

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

Discontinued Для TemplateField можно использовать элемент CheckBox, уже определенный ItemTemplateв . Просто задайте для свойства ID значение Не поддерживается, а для свойства Enabled — значение true.

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

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

Примечание

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

Перетащите раскрывающийся список из панели элементов в CategoryName templateField, ItemTemplateзадав для нее ID значение Categories. На этом этапе мы обычно определяем источник данных DropDownLists с помощью смарт-тега, создавая объект ObjectDataSource. Однако при этом объект ObjectDataSource будет добавлен в ItemTemplate, что приведет к созданию экземпляра ObjectDataSource для каждой строки GridView. Вместо этого давайте создадим ObjectDataSource за пределами TemplateFields GridView. Завершите редактирование шаблона и перетащите объект ObjectDataSource из панели элементов в Designer под ProductsDataSource ObjectDataSource. Присвойте новому объекту имя ObjectDataSource CategoriesDataSource и настройте его для использования CategoriesBLL метода класса s 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 DropDownList выводит список всех категорий, но пока не выбирает соответствующую категорию для продукта, привязанного к строке GridView. Для этого необходимо задать Categories в раскрывающемся списке SelectedValue значение продукта CategoryID . Щелкните ссылку Изменить DataBindings в смарт-теге DropDownList и свяжите SelectedValue свойство с полем CategoryID данных, как показано на рисунке 12.

Привяжите значение CategoryID продукта к свойству SelectedValue в раскрывающемся списке

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

Последняя проблема остается: если продукт не имеет указанного CategoryID значения, инструкция привязки данных в SelectedValue приведет к исключению. Это связано с тем, что DropDownList содержит только элементы для категорий и не предлагает вариант для тех продуктов, которые имеют NULL значение базы данных для CategoryID. Чтобы устранить эту проблему, задайте свойству DropDownList AppendDataBoundItems значение true и добавьте новый элемент в 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 в качестве источника данных, CategoriesBLL метод класса GetCategories будет вызываться n раз за посещение страницы, где n — это количество строк в GridView. Эти вызовы n приводят к GetCategoriesn запросам к базе данных. Это влияние на базу данных можно уменьшить путем кэширования возвращаемых категорий либо в кэше для каждого запроса, либо с помощью уровня кэширования с использованием зависимости кэширования SQL или очень короткого срока действия.

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

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

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

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

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

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

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

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

По центру флажка

Рис. 15. Центрирование Discontinued checkBox

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

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

После внесения указанных выше изменений форматирования, добавления элементов управления Кнопка и удаления ненужных 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 показана эта страница при просмотре в браузере после добавления веб-элементов управления "Кнопка" и изменения форматирования.

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

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

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

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

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

private void BatchUpdate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Find the ProductsRow instance in products that maps to gvRow
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        Northwind.ProductsRow product = products.FindByProductID(productID);
        if (product != null)
        {
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateWithTransaction(products);
}

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

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

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

Примечание

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

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

Для работы с этим руководством необходимо BatchUpdate вызвать метод при нажатии любой из кнопок Обновить продукты. Создайте обработчики событий для Click этих двух элементов управления Button и добавьте следующий код в обработчики событий:

BatchUpdate();
ClientScript.RegisterStartupScript(this.GetType(), "message", 
    "alert('The products have been updated.');", true);

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

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

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

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

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

private void BatchUpdateAlternate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Create a new ProductRow instance
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        
        Northwind.ProductsDataTable currentProductDataTable = 
            productsAPI.GetProductByProductID(productID);
        if (currentProductDataTable.Rows.Count > 0)
        {
            Northwind.ProductsRow product = currentProductDataTable[0];
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
            // Import the ProductRow into the products DataTable
            products.ImportRow(product);
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateProductsWithTransaction(products);
}

BatchMethodAlternate Начинается с создания пустого ProductsDataTable экземпляра с именем products. Затем он выполняет шаги по коллекции GridView Rows и для каждой строки получает сведения о конкретном продукте с помощью метода BLL s GetProductByProductID(productID) . Свойства извлеченного экземпляра ProductsRow обновляются так же, как BatchUpdateи , но после обновления строки он импортируется в products``ProductsDataTable с помощью метода DataTable.ImportRow(DataRow)

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

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

public int UpdateProductsWithTransaction(Northwind.ProductsDataTable products)
{
    // Mark each product as Modified
    products.AcceptChanges();
    foreach (Northwind.ProductsRow product in products)
        product.SetModified();
    // Update the data via a transaction
    return UpdateWithTransaction(products);
}

Сводка

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

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

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

Об авторе

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

Особая благодарность

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