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


Пакетная вставка (VB)

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

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

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

Введение

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

Эта концепция также может применяться при добавлении записей. Представьте, что здесь, в Northwind Traders, мы обычно получаем поставки от поставщиков, которые содержат ряд продуктов для определенной категории. Например, мы можем получить партию шести различных чайных и кофейных продуктов от Tokyo Traders. Если пользователь вводит шесть продуктов по одному через элемент управления DetailsView, ей придется снова и снова выбирать множество одинаковых значений: ей нужно будет выбрать одну и ту же категорию (Напитки), одного поставщика (Tokyo Traders), то же значение с отменой (False) и те же единицы в значении заказа (0). Такая повторяющаяся запись данных не только занимает много времени, но и может приводить к ошибкам.

С небольшой работой мы можем создать интерфейс пакетной вставки, который позволяет пользователю выбрать поставщика и категорию один раз, ввести ряд названий продуктов и цен на единицы, а затем нажать кнопку, чтобы добавить новые продукты в базу данных (см. рис. 1). При добавлении каждого продукта ему ProductName и UnitPrice полям данных присваиваются значения, введенные в TextBoxes, а значениям CategoryID и SupplierID — значения из DropDownLists в верхней части формы. Для значений Discontinued и UnitsOnOrder устанавливаются жестко заданные значения False и 0 соответственно.

Интерфейс пакетной вставки

Рис. 1. Интерфейс пакетной вставки (щелкните для просмотра полноразмерного изображения)

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

Шаг 1. Создание интерфейса отображения

Это руководство будет состоять из одной страницы, разделенной на два региона: область отображения и область вставки. Интерфейс отображения, который мы создадим на этом шаге, отображает продукты в GridView и включает кнопку с названием Обработка отгрузки продукта. При нажатии этой кнопки интерфейс отображения заменяется интерфейсом вставки, как показано на рисунке 1. Интерфейс отображения возвращается после нажатия кнопок Add Products from Shipment (Добавить продукты из отгрузки) или Cancel (Отмена). Мы создадим интерфейс вставки на шаге 2.

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

Начните с открытия BatchInsert.aspx страницы в папке BatchData и перетащите панель из панели элементов на Designer (см. рис. 2). Присвойте свойству Panel значение IDDisplayInterface. При добавлении панели в Designer свойства Height и Width задаются равными 50 и 125 пикселей соответственно. Удалите эти значения свойств из окно свойств.

Перетащите панель из панели элементов на Designer

Рис. 2. Перетащите панель из панели элементов на Designer (щелкните для просмотра полноразмерного изображения)

Затем перетащите элементы управления Button и GridView в панель. Присвойте свойству Button s ID значение , ProcessShipment а его Text свойству — значение Обработка отгрузки продуктов. Присвойте ID свойству GridView значение ProductsGrid и привяжите его из смарт-тега к новому объекту ObjectDataSource с именем ProductsDataSource. Настройте ObjectDataSource для извлечения данных из ProductsBLL метода класса s GetProducts . Так как gridView используется только для отображения данных, задайте для раскрывающихся списков на вкладках UPDATE, INSERT и DELETE значение (Нет). Нажмите кнопку Готово, чтобы завершить работу мастера настройки источника данных.

Отображение данных, возвращаемых методом GetProducts класса ProductsBLL

Рис. 3. Отображение данных, возвращенных методом ProductsBLL Classs GetProducts (щелкните для просмотра полноразмерного изображения)

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

Рис. 4. Задайте для Drop-Down Списки на вкладках UPDATE, INSERT и DELETE значение (Нет) (Щелкните для просмотра полноразмерного изображения)

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

После добавления элементов управления Panel, Button, GridView и ObjectDataSource и настройки полей GridView декларативная разметка страницы должна выглядеть примерно так:

<asp:Panel ID="DisplayInterface" runat="server">
    <p>
        <asp:Button ID="ProcessShipment" runat="server" 
            Text="Process Product Shipment" /> 
    </p>
    <asp:GridView ID="ProductsGrid" runat="server" AllowPaging="True" 
        AllowSorting="True" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
        <Columns>
            <asp:BoundField DataField="ProductName" HeaderText="Product" 
                SortExpression="ProductName" />
            <asp:BoundField DataField="CategoryName" HeaderText="Category" 
                ReadOnly="True" SortExpression="CategoryName" />
            <asp:BoundField DataField="SupplierName" HeaderText="Supplier" 
                ReadOnly="True" SortExpression="SupplierName" />
            <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" 
                HeaderText="Price" HtmlEncode="False" 
                SortExpression="UnitPrice">
                <ItemStyle HorizontalAlign="Right" />
            </asp:BoundField>
            <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" 
                SortExpression="Discontinued">
                <ItemStyle HorizontalAlign="Center" />
            </asp:CheckBoxField>
        </Columns>
    </asp:GridView>
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
</asp:Panel>

Обратите внимание, что разметка для Button и GridView отображается в открывающем и закрывающем <asp:Panel> тегах. Так как эти элементы управления находятся в DisplayInterface Панели, их можно скрыть, просто задав свойству Panel s Visible значение False. На шаге 3 рассматривается программное изменение свойства Panel Visible в ответ на нажатие кнопки, чтобы отобразить один интерфейс при скрытии другого.

Уделите немного времени, чтобы просмотреть наш прогресс в браузере. Как показано на рисунке 5, над элементом GridView должна появиться кнопка Процесс отправки продукта, которая содержит список продуктов по десять за раз.

GridView Списки возможности сортировки и разбиения по страницам продуктов и предложений

Рис. 5. GridView Списки возможности сортировки и разбиения по страницам продуктов и предложений (щелкните для просмотра полноразмерного изображения)

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

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

Сначала перетащите панель из панели элементов на Designer, разместив ее под существующей DisplayInterface панелью. Присвойте свойству ID этой добавленной панели значение InsertingInterface , а его Visible свойству — значение False. Мы добавим код, который задает свойству InsertingInterface Panel Visible значение True на шаге 3. Кроме того, очистите значения свойств Panel и HeightWidth .

Далее необходимо создать интерфейс вставки, показанный на рис. 1. Этот интерфейс можно создать с помощью различных методов HTML, но мы будем использовать довольно простую таблицу: таблицу из четырех столбцов и семи строк.

Примечание

При вводе разметки для элементов HTML <table> я предпочитаю использовать исходное представление. Хотя в Visual Studio есть средства для добавления <table> элементов через Designer, Designer, кажется, слишком готовы внедрять в разметку не заданные параметрыstyle. После создания разметки <table> я обычно возвращаюсь к Designer, чтобы добавить веб-элементы управления и задать их свойства. При создании таблиц с предварительно определенными столбцами и строками я предпочитаю использовать статический HTML-код, а не элемент управления "Веб-таблица ", так как доступ к любым веб-элементам управления, размещенным в элементе управления "Веб-таблица", можно получить только с помощью FindControl("controlID") шаблона. Однако я использую элементы управления Table Web для таблиц динамического размера (те, строки или столбцы которых основаны на определенных базах данных или пользовательских критериях), так как элемент управления Table Web можно создать программным способом.

Введите следующую разметку в <asp:Panel> тегах InsertingInterface панели:

<table class="DataWebControlStyle" cellspacing="0">
    <tr class="BatchInsertHeaderRow">
        <td class="BatchInsertLabel">Supplier:</td>
        <td></td>
        <td class="BatchInsertLabel">Category:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertAlternatingRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertAlternatingRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertFooterRow">
        <td colspan="4">
        </td>
    </tr>
</table>

Эта <table> разметка пока не включает веб-элементы управления, мы добавим их на мгновение. Обратите внимание, что каждый <tr> элемент содержит определенный параметр класса CSS: для строки верхнего колонтитула, BatchInsertHeaderRow в которую будет отправляться поставщик и категория DropDownLists; BatchInsertFooterRow для строки нижнего колонтитула, в которой будут отправляться кнопки Add Products from Shipment (Добавить продукты из отгрузки) и Cancel (Отмена), а также чередование BatchInsertRow значений и BatchInsertAlternatingRow значений для строк, которые будут содержать элементы управления TextBox с ценами на продукт и за единицу. Я создал соответствующие классы CSS в Styles.css файле, чтобы придать интерфейсу вставки вид, аналогичный элементам управления GridView и DetailsView, которые мы использовали в этих руководствах. Эти классы CSS показаны ниже.

/*** Styles for ~/BatchData/BatchInsert.aspx tutorial ***/
.BatchInsertLabel
{
    font-weight: bold;
    text-align: right;
}
.BatchInsertHeaderRow td
{
    color: White;
    background-color: #900;
    padding: 11px;
}
.BatchInsertFooterRow td
{
    text-align: center;
    padding-top: 5px;
}
.BatchInsertRow
{
}
.BatchInsertAlternatingRow
{
    background-color: #fcc;
}

После ввода этой разметки вернитесь в конструктор. Он <table> должен отображаться в виде таблицы из четырех столбцов и семи строк в Designer, как показано на рисунке 6.

Интерфейс вставки состоит из четырех столбцов, Seven-Row таблицы

Рис. 6. Интерфейс вставки состоит из четырех столбцов, Seven-Row таблицы (щелкните для просмотра полноразмерного изображения)

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

Присвойте свойству поставщика DropDownList ID значение Suppliers и привяжите его к новому объекту ObjectDataSource с именем SuppliersDataSource. Настройте новый объект ObjectDataSource для получения данных из SuppliersBLL метода класса GetSuppliers и задайте для раскрывающегося списка update tab s значение (Нет). Чтобы завершить работу мастера, нажмите кнопку Готово.

Настройка ObjectDataSource для использования метода GetSuppliers класса SuppliersBLL

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

Чтобы в Suppliers раскрывающемся списке отображалось CompanyName поле данных, и в SupplierID качестве значений ListItem используйте поле данных.

Отображение поля данных CompanyName и использование идентификатора поставщика в качестве значения

Рис. 8. Отображение CompanyName поля данных и использование SupplierID в качестве значения (щелкните для просмотра полноразмерного изображения)

Назовите второй элемент DropDownList Categories и привяжите его к новому объекту ObjectDataSource с именем CategoriesDataSource. CategoriesDataSource Настройте ObjectDataSource для использования CategoriesBLL метода класса GetCategories ; установите для раскрывающихся списков на вкладках UPDATE и DELETE значение (Нет) и нажмите кнопку Готово, чтобы завершить работу мастера. Наконец, в раскрывающемся списке отображается CategoryName поле данных и в качестве значения используется CategoryID .

После добавления этих двух раскрывающихся списков и привязки к соответствующим образом настроенным ObjectDataSources экран должен выглядеть примерно так, как на рисунке 9.

Строка заголовка теперь содержит раскрывающийся список поставщиков и категорий

Рис. 9. Строка заголовка теперь содержит раскрывающийся Suppliers список и Categories (щелкните для просмотра полноразмерного изображения)

Теперь нам нужно создать textBoxes, чтобы получить имя и цену для каждого нового продукта. Перетащите элемент управления TextBox из панели элементов в Designer для каждой из пяти строк названия продукта и цен. ID Задайте свойства TextBoxes равными ProductName1, UnitPrice1, ProductName2, UnitPrice2, ProductName3, UnitPrice3и т. д.

Добавьте CompareValidator после каждого элемента управления TextBoxes цены за единицу, задав ControlToValidate для свойства соответствующее IDзначение . Присвойте свойству OperatorGreaterThanEqualзначение , ValueToCompare значение 0 и Type значение Currency. Эти параметры указывают CompareValidator, чтобы гарантировать, что цена, если она введена, является допустимым значением в валюте, которое больше или равно нулю. Задайте для Text свойства значение *, а ErrorMessage — значение Цена должна быть больше или равна нулю. Кроме того, опустите символы валют.

Примечание

Интерфейс вставки не включает элементы управления RequiredFieldValidator, даже если ProductName поле в Products таблице базы данных не допускает NULL значения. Это связано с тем, что мы хотим разрешить пользователю вводить до пяти продуктов. Например, если пользователь указал название продукта и цену за единицу для первых трех строк, оставив последние две строки пустыми, мы просто добавим три новых продукта в систему. Однако ProductName мы должны программно проверка, чтобы убедиться, что при вводе цены за единицу указано соответствующее значение названия продукта. Мы рассмотрим эту проверка на шаге 4.

При проверке введенных пользователем данных CompareValidator сообщает о недопустимых данных, если значение содержит символ валюты. Добавьте $ перед каждой из цен за единицу TextBoxes, чтобы служить визуальным сигналом, который указывает пользователю опустить символ валюты при вводе цены.

Наконец, добавьте элемент управления ValidationSummary в InsertingInterface Панель, задав свойству ShowMessageBox значение True , а свойству ShowSummary — значение False. При использовании этих параметров, если пользователь вводит недопустимое значение цены за единицу, рядом с элементами управления TextBox появится звездочка, а в разделе ValidationSummary будет отображаться клиентское окно сообщений, в котором отображается сообщение об ошибке, указанное ранее.

На этом этапе экран должен выглядеть примерно так, как на рис. 10.

Интерфейс вставки теперь включает текстовые поля для названий и цен продуктов

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

Далее необходимо добавить кнопки Add Products from Shipment (Добавить продукты из отправки) и Cancel (Отмена) в строку нижнего колонтитула. Перетащите два элемента управления Button из панели элементов в нижний колонтитул интерфейса вставки, задав свойствам AddProducts Button ID значения и CancelButton и Text Значение Добавить продукты из отгрузки и Отмена соответственно. Кроме того, присвойте свойству CancelButton элемента управления CausesValidation значение false.

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

Перетащите элемент управления Label Web из панели элементов в верхнюю часть страницы в Designer. Присвойте свойству ID значение StatusLabel, очистите Text свойство и задайте для Visible свойств и EnableViewState значение False. Как мы видели в предыдущих руководствах, установка EnableViewState свойства в False значение позволяет программно изменить значения свойства Label и автоматически отменить изменения вернуться к значениям по умолчанию при последующей обратной отправке. Это упрощает отображение сообщения о состоянии в ответ на действие пользователя, которое исчезает при последующей обратной отправке. Наконец, присвойте свойству StatusLabel элемента управления CssClass значение Warning, которое является именем класса CSS, определенного в Styles.css , который отображает текст крупным курсивом, полужирным, красным шрифтом.

На рисунке 11 показана Designer Visual Studio после добавления и настройки метки.

Поместите элемент управления StatusLabel над двумя элементами управления panel

Рис. 11. Поместите StatusLabel элемент управления над двумя панелями управления (щелкните для просмотра полноразмерного изображения)

Шаг 3. Переключение между интерфейсами отображения и вставки

На этом этапе мы завершили разметку для интерфейсов отображения и вставки, но мы по-прежнему остались с двумя задачами:

  • Переключение между интерфейсами отображения и вставки
  • Добавление продуктов из отправки в базу данных

В настоящее время интерфейс отображения виден, но интерфейс вставки скрыт. Это связано с тем, что свойству DisplayInterface Panel Visible присвоено значение True (значение по умолчанию), а свойству InsertingInterface Panel Visible — значение False. Чтобы переключаться между двумя интерфейсами, необходимо просто переключить значение свойства каждого элемента управления Visible .

Мы хотим перейти от интерфейса отображения к интерфейсу вставки при нажатии кнопки Обработка отгрузки продукта. Поэтому создайте обработчик событий для этого события Button Click , который содержит следующий код:

Protected Sub ProcessShipment_Click(sender As Object, e As EventArgs) _
    Handles ProcessShipment.Click
    DisplayInterface.Visible = False
    InsertingInterface.Visible = True
End Sub

Этот код просто скрывает DisplayInterface панель и отображает панель InsertingInterface .

Затем создайте обработчики событий для элементов управления Add Products from Shipment (Добавить продукты из отправки) и Cancel Button (Кнопка отмены) в интерфейсе вставки. При нажатии любой из этих кнопок необходимо отменить изменения обратно к интерфейсу отображения. Создайте Click обработчики событий для обоих элементов управления Button, чтобы они вызывали ReturnToDisplayInterfaceметод , который мы добавим мгновенно. Помимо скрытия InsertingInterface панели и отображения DisplayInterface панели метод ReturnToDisplayInterface должен вернуть веб-элементы управления в состояние предварительного редактирования. Для этого необходимо задать для свойств DropDownLists SelectedIndex значение 0 и очистить Text свойства элементов управления TextBox.

Примечание

Подумайте, что может произойти, если мы не вернем элементы управления в состояние предварительного редактирования, прежде чем вернуться в интерфейс отображения. Пользователь может нажать кнопку Process Product Shipment (Обработка отгрузки продукта), ввести продукты из отгрузки, а затем нажать кнопку Добавить продукты из отправки. Это приведет к добавлению продуктов и возврату пользователя в интерфейс отображения. На этом этапе пользователю может потребоваться добавить еще одну отправку. После нажатия кнопки Процесс отправки продукта они будут возвращаться в интерфейс вставки, но значения выбора DropDownList и TextBox по-прежнему будут заполнены их предыдущими значениями.

Protected Sub AddProducts_Click(sender As Object, e As EventArgs) _
    Handles AddProducts.Click
    ' TODO: Save the products
    ' Revert to the display interface
    ReturnToDisplayInterface()
End Sub
Protected Sub CancelButton_Click(sender As Object, e As EventArgs) _
    Handles CancelButton.Click
    ' Revert to the display interface
    ReturnToDisplayInterface()
End Sub
Const firstControlID As Integer = 1
Const lastControlID As Integer = 5
Private Sub ReturnToDisplayInterface()
    ' Reset the control values in the inserting interface
    Suppliers.SelectedIndex = 0
    Categories.SelectedIndex = 0
    For i As Integer = firstControlID To lastControlID
        CType(InsertingInterface.FindControl _
            ("ProductName" + i.ToString()), TextBox).Text = String.Empty
        CType(InsertingInterface.FindControl _
            ("UnitPrice" + i.ToString()), TextBox).Text = String.Empty
    Next
    DisplayInterface.Visible = True
    InsertingInterface.Visible = False
End Sub

Оба Click обработчика событий просто вызывают ReturnToDisplayInterface метод , хотя мы вернемся к обработчику событий Add Products from Shipment Click на шаге 4 и добавим код для сохранения продуктов. ReturnToDisplayInterface Начинается с возврата раскрывающихся Suppliers списков и Categories к первым параметрам. Две константы firstControlID и lastControlID помечают начальные и конечные значения индекса элементов управления, используемые при именовании названия продукта и цены за единицу TextBox в интерфейсе вставки и используются в границах For цикла, который задает Text свойства элементов управления TextBox в пустую строку. Наконец, свойства Panels Visible сбрасываются, чтобы интерфейс вставки был скрыт, а интерфейс отображения отображается.

Проверьте эту страницу в браузере. При первом посещении страницы вы увидите интерфейс отображения, как показано на рисунке 5. Нажмите кнопку Обработать отправку продукта. Страница будет обратной передачи, и теперь вы увидите интерфейс вставки, как показано на рисунке 12. При нажатии кнопки Add Products from Shipment (Добавить продукты из отгрузки) или Cancel (Отмена) вы перейдете к интерфейсу отображения.

Примечание

При просмотре интерфейса вставки проверьте параметры CompareValidators на полях TextBoxes цены за единицу. При нажатии кнопки Add Products from Shipment (Добавить продукты из отгрузки) при нажатии кнопки Add Products from Shipment (Добавить продукты из отгрузки) должно появиться предупреждение о недопустом значении валюты или ценах со значением меньше нуля.

Интерфейс вставки отображается после нажатия кнопки

Рис. 12. Интерфейс вставки отображается после нажатия кнопки "Обработка отгрузки продукта" (нажмите для просмотра полноразмерного изображения)

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

Все, что осталось для этого руководства, — сохранить продукты в базе данных в обработчике Click событий Add Products from Shipment Button. Это можно сделать, создав ProductsDataTable и добавив ProductsRow экземпляр для каждого из предоставленных имен продуктов. После добавления этих ProductsRow команд мы будем выполнять вызов ProductsBLL метода класса , UpdateWithTransaction передавая ProductsDataTableв . Помните, что UpdateWithTransaction метод, который был создан еще в руководстве по переносу изменений базы данных в рамках транзакции, передает ProductsDataTableProductsTableAdapterв метод .UpdateWithTransaction После этого запускается транзакция ADO.NET, и TableAdapter выдает инструкцию INSERT в базу данных для каждого добавленного ProductsRow в таблицу DataTable. При условии, что все продукты добавляются без ошибок, транзакция фиксируется, в противном случае выполняется откат.

Код для обработчика Click событий Add Products from Shipment Button также должен выполнять проверку ошибок. Так как в интерфейсе вставки не используется requiredFieldValidators, пользователь может ввести цену на продукт, опустив его имя. Так как название продукта является обязательным, в случае развертывания такого условия необходимо предупредить пользователя и не продолжать вставку. Полный Click код обработчика событий выглядит следующим образом:

Protected Sub AddProducts_Click(sender As Object, e As EventArgs) _
    Handles AddProducts.Click
    ' Make sure that the UnitPrice CompareValidators report valid data...
    If Not Page.IsValid Then Exit Sub
    ' Add new ProductsRows to a ProductsDataTable...
    Dim products As New Northwind.ProductsDataTable()
    For i As Integer = firstControlID To lastControlID
        ' Read in the values for the product name and unit price
        Dim productName As String = CType(InsertingInterface.FindControl _
            ("ProductName" + i.ToString()), TextBox).Text.Trim()
        Dim unitPrice As String = CType(InsertingInterface.FindControl _
            ("UnitPrice" + i.ToString()), TextBox).Text.Trim()
        ' Ensure that if unitPrice has a value, so does productName
        If unitPrice.Length > 0 AndAlso productName.Length = 0 Then
            ' Display a warning and exit this event handler
            StatusLabel.Text = "If you provide a unit price you must also 
                                include the name of the product."
            StatusLabel.Visible = True
            Exit Sub
        End If
        ' Only add the product if a product name value is provided
        If productName.Length > 0 Then
            ' Add a new ProductsRow to the ProductsDataTable
            Dim newProduct As Northwind.ProductsRow = products.NewProductsRow()
            ' Assign the values from the web page
            newProduct.ProductName = productName
            newProduct.SupplierID = Convert.ToInt32(Suppliers.SelectedValue)
            newProduct.CategoryID = Convert.ToInt32(Categories.SelectedValue)
            If unitPrice.Length > 0 Then
                newProduct.UnitPrice = Convert.ToDecimal(unitPrice)
            End If
            ' Add any "default" values
            newProduct.Discontinued = False
            newProduct.UnitsOnOrder = 0
            products.AddProductsRow(newProduct)
        End If
    Next
    ' If we reach here, see if there were any products added
    If products.Count > 0 Then
        ' Add the new products to the database using a transaction
        Dim productsAPI As New ProductsBLL()
        productsAPI.UpdateWithTransaction(products)
        ' Rebind the data to the grid so that the products just added are displayed
        ProductsGrid.DataBind()
        ' Display a confirmation (don't use the Warning CSS class, though)
        StatusLabel.CssClass = String.Empty
        StatusLabel.Text = String.Format( _
            "{0} products from supplier {1} have been " & _
            "added and filed under category {2}.", _
            products.Count, Suppliers.SelectedItem.Text, Categories.SelectedItem.Text)
        StatusLabel.Visible = True
        ' Revert to the display interface
        ReturnToDisplayInterface()
    Else
        ' No products supplied!
        StatusLabel.Text = 
            "No products were added. Please enter the " & _
            "product names and unit prices in the textboxes."
        StatusLabel.Visible = True
    End If
End Sub

Обработчик событий начинается с того, что Page.IsValid свойство возвращает значение True. Если возвращается Falseзначение , это означает, что один или несколько объектов CompareValidators сообщают недопустимые данные. В этом случае мы не хотим пытаться вставить введенные продукты, иначе при попытке назначить введенное пользователем значение цены за единицу свойству ProductsRow s UnitPrice в конечном итоге возникнет исключение.

Затем создается новый ProductsDataTable экземпляр (products). Цикл For используется для итерации по названию продукта и цене за единицу TextBoxes, а Text свойства считываются в локальные переменные productName и unitPrice. Если пользователь ввел значение для цены за единицу, но не для соответствующего названия продукта, StatusLabel отображается сообщение Если вы указали цену за единицу, необходимо также указать имя продукта, и обработчик событий будет завершен.

Если указано название продукта, создается новый ProductsRow экземпляр с помощью ProductsDataTable метода s NewProductsRow . Этому новому ProductsRow свойству экземпляра ProductName присваивается текущее имя продукта TextBox, а SupplierID свойства и CategoryIDSelectedValue свойствам DropDownLists в заголовке интерфейса вставки. Если пользователь ввел значение для цены продукта, оно назначается свойству ProductsRow экземпляра UnitPrice ; в NULL противном случае свойство остается неназначимым, что приведет к тому, что в базе данных будет задано значение UnitPrice . Наконец, Discontinued свойствам и UnitsOnOrder присваиваются жестко заданные значения False и 0 соответственно.

После назначения свойств экземпляру ProductsRow он добавляется в ProductsDataTable.

По завершении For цикла мы проверка, были ли добавлены какие-либо продукты. Пользователь может, в конце концов, щелкнуть Добавить продукты из отгрузки перед вводом каких-либо названий продуктов или цен. Если в ProductsDataTableсодержится хотя бы один продукт , ProductsBLL вызывается метод класса UpdateWithTransaction . Затем данные отскокируются в ProductsGrid GridView, чтобы добавленные продукты отображались в интерфейсе отображения. Обновляется StatusLabel для отображения сообщения подтверждения и ReturnToDisplayInterface вызывается , скрывая вставляемый интерфейс и отображая интерфейс отображения.

Если продукты не были введены, интерфейс вставки остается отображаемым, но отображается сообщение Нет продуктов были добавлены. Введите названия продуктов и цены на единицы в отображаемых текстовых полях.

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

Имя продукта требуется при вводе цены за единицу

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

Три новых veggies были добавлены для поставщика Mayumi s

Рис. 14. Три новых veggies have been added for the Supplier Mayumi s (Щелкните, чтобы просмотреть полноразмерное изображение)

Новые продукты можно найти на последней странице GridView

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

Примечание

Логика пакетной вставки, используемая в этом руководстве, заключает вставки в область транзакции. Чтобы проверить это, намеренно введите ошибку на уровне базы данных. Например, вместо назначения свойства нового ProductsRow экземпляра CategoryID выбранному значению в Categories DropDownList присвойте ему такое значение, как i * 5. Ниже i приведен индексатор цикла со значениями в диапазоне от 1 до 5. Таким образом, при добавлении двух или более продуктов в пакетной вставке первый продукт будет иметь допустимое CategoryID значение (5), но последующие продукты будут иметь CategoryID значения, которые не совпадают со CategoryID значениями в Categories таблице. Результатом является то, что в то время как первый INSERT из них будет успешным, последующие из них будут завершаться сбоем с нарушением ограничения внешнего ключа. Так как пакетная вставка является атомарной, первый INSERT будет откатирован, возвращая базу данных в ее состояние до начала процесса пакетной вставки.

Сводка

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

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

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

Об авторе

Скотт Митчелл (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.