Пакетная вставка (C#)
Узнайте, как вставить несколько записей базы данных за одну операцию. На уровне пользовательского интерфейса мы расширяем GridView, чтобы разрешить пользователю вводить несколько новых записей. На уровне доступа к данным мы заключаем в оболочку несколько операций вставки в транзакции, чтобы убедиться, что все вставки успешно выполнены или все вставки откатываются.
Введение
В учебнике пакетное обновление мы рассмотрели настройку элемента управления GridView для представления интерфейса, в котором можно редактировать несколько записей. Пользователь, посещающий страницу, может внести ряд изменений, а затем одним нажатием кнопки выполнить пакетное обновление. В ситуациях, когда пользователи обычно обновляют несколько записей за один раз, такой интерфейс может сохранять бесчисленные щелчки и переключения контекста между клавиатурой и мышью по сравнению с функциями редактирования строк по умолчанию, которые впервые были рассмотрены в руководстве Общие сведения о вставке, обновлении и удалении данных .
Эта концепция также может применяться при добавлении записей. Представьте, что здесь, в Northwind Traders, мы обычно получаем поставки от поставщиков, которые содержат ряд продуктов для определенной категории. Например, мы можем получить партию шести различных продуктов чая и кофе от Tokyo Traders. Если пользователь вводит шесть продуктов по одному с помощью элемента управления DetailsView, ей придется снова и снова выбирать многие из одинаковых значений: ей потребуется выбрать одну и ту же категорию (Напитки), одного поставщика (Tokyo Traders), то же значение, которое прекращено (False) и те же единицы в значении заказа (0). Этот повторяющийся ввод данных не только занимает много времени, но и может приводить к ошибкам.
С небольшой работой мы можем создать интерфейс пакетной вставки, который позволяет пользователю выбрать поставщика и категорию один раз, ввести ряд названий продуктов и цены на единицы, а затем нажать кнопку, чтобы добавить новые продукты в базу данных (см. рис. 1). При добавлении каждого продукта его полям данных и UnitPrice
присваиваются значения, ProductName
введенные в 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). Присвойте свойству Panels ID
значение DisplayInterface
. При добавлении Panel в Designer свойства Height
и Width
задаются равными 50 и 125 пикселей соответственно. Очистите эти значения свойств из окно свойств.
Рис. 2. Перетащите панель с панели элементов на Designer (щелкните для просмотра полноразмерного изображения)
Затем перетащите элементы управления Button и GridView в панель. Присвойте свойству ProcessShipment
Button значение ID
, а его Text
свойству — значение Обработка отправки продукта. Задайте для свойства GridView ID
значение ProductsGrid
и привяжите его из смарт-тега к новому объекту ObjectDataSource с именем ProductsDataSource
. Настройте ObjectDataSource для извлечения своих данных из ProductsBLL
метода класса .GetProducts
Так как gridView используется только для отображения данных, задайте для раскрывающихся списков на вкладках UPDATE, INSERT и DELETE значение (Нет). Нажмите кнопку Готово, чтобы завершить работу мастера настройки источника данных.
Рис. 3. Отображение данных, возвращаемых методом ProductsBLL
Class s GetProducts
(щелкните для просмотра полноразмерного изображения)
Рис. 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 значение Visible
false
. На шаге 3 рассматривается программное изменение свойства Panel Visible
в ответ на нажатие кнопки для отображения одного интерфейса при скрытии другого.
Найдите минутку, чтобы просмотреть наш прогресс через браузер. Как показано на рисунке 5, вы увидите кнопку Процесс отправки продукта над GridView, которая выводит список продуктов по десять за раз.
Рис. 5. GridView Списки возможности сортировки и разбиения продуктов и предложений (щелкните, чтобы просмотреть полноразмерное изображение)
Шаг 2. Создание интерфейса вставки
После завершения интерфейса отображения мы готовы к созданию интерфейса вставки. В этом руководстве мы создадим интерфейс вставки, который запрашивает одно значение поставщика и категории, а затем позволяет пользователю ввести до пяти названий продуктов и цен за единицу. С помощью этого интерфейса пользователь может добавить один или пять новых продуктов, которые используют одну и ту же категорию и поставщика, но имеют уникальные названия продуктов и цены.
Сначала перетащите панель из панели элементов на Designer, поместив ее под существующую DisplayInterface
панель. Присвойте свойству ID
этой добавленной панели значение InsertingInterface
, а его Visible
свойству — значение false
. Мы добавим код, который задает InsertingInterface
для свойства Panel значение true
Visible
на шаге 3. Также очистите значения свойств Panel и Height
Width
.
Далее необходимо создать интерфейс вставки, показанный на рис. 1. Этот интерфейс можно создать с помощью различных методов HTML, но мы будем использовать довольно простую таблицу: таблицу с четырьмя столбцами, семь строк.
Примечание
При вводе разметки для элементов HTML <table>
я предпочитаю использовать представление Source. Хотя в Visual Studio есть средства для добавления <table>
элементов через Designer, Designer, кажется, слишком готовы внедрять в разметку не заданные style
параметры. После создания разметки <table>
я обычно возвращаюсь к Designer, чтобы добавить веб-элементы управления и задать их свойства. При создании таблиц с предварительно определенными столбцами и строками я предпочитаю использовать статический HTML-код, а не элемент управления "Веб-таблица ", так как любые веб-элементы управления, размещенные в элементе управления "Веб-таблица", можно получить доступ только с помощью FindControl("controlID")
шаблона. Однако я использую элементы управления "Веб-таблица" для таблиц динамического размера (те, строки или столбцы которых основаны на определенных базах данных или пользовательских критериях), так как веб-элемент управления "Таблица" можно создавать программными средствами.
Введите следующую разметку в <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
для строки нижнего колонтитула, в которой будут перенаправляться кнопки Добавления продуктов из кнопок "Отправка" и "Отмена", а также для чередующихся 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.
Рис. 6. Интерфейс вставки состоит из четырех столбцов, Seven-Row таблицы (щелкните для просмотра полноразмерного изображения)
Теперь мы готовы добавить веб-элементы управления в интерфейс вставки. Перетащите два списка DropDownList с панели элементов в соответствующие ячейки таблицы: один для поставщика и один для категории.
Задайте для свойства поставщика DropDownList ID
значение Suppliers
и привяжите его к новому объекту ObjectDataSource с именем SuppliersDataSource
. Настройте новый объект ObjectDataSource для получения данных из SuppliersBLL
метода класса GetSuppliers
и задайте для раскрывающегося списка update tab s значение (Нет). Чтобы завершить работу мастера, нажмите кнопку Готово.
Рис. 7. Настройка ObjectDataSource для использования SuppliersBLL
метода Класса GetSuppliers
(щелкните для просмотра полноразмерного изображения)
В Раскрывающемся Suppliers
списке отображается CompanyName
поле данных и используется SupplierID
поле данных в качестве значений ListItem
.
Рис. 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
значение . Присвойте свойству Operator
GreaterThanEqual
значение , 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 после добавления и настройки метки.
Рис. 11. Поместите StatusLabel
элемент управления над двумя панелями управления (щелкните для просмотра полноразмерного изображения)
Шаг 3. Переключение между интерфейсами отображения и вставки
На этом этапе мы завершили разметку для интерфейсов отображения и вставки, но мы по-прежнему остались с двумя задачами:
- Переключение между интерфейсами отображения и вставки
- Добавление продуктов из отправки в базу данных
В настоящее время интерфейс отображения виден, но интерфейс вставки скрыт. Это связано с тем, что свойству DisplayInterface
Panel Visible
присвоено значение true
(значение по умолчанию), а свойству InsertingInterface
Panel Visible
— значение false
. Чтобы переключаться между двумя интерфейсами, необходимо просто переключить значение свойства каждого элемента управления Visible
.
Мы хотим перейти от интерфейса отображения к интерфейсу вставки при нажатии кнопки Обработка отгрузки продукта. Поэтому создайте обработчик событий для этого события Button Click
, который содержит следующий код:
protected void ProcessShipment_Click(object sender, EventArgs e)
{
DisplayInterface.Visible = false;
InsertingInterface.Visible = true;
}
Этот код просто скрывает 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 void AddProducts_Click(object sender, EventArgs e)
{
// TODO: Save the products
// Revert to the display interface
ReturnToDisplayInterface();
}
protected void CancelButton_Click(object sender, EventArgs e)
{
// Revert to the display interface
ReturnToDisplayInterface();
}
const int firstControlID = 1;
const int lastControlID = 5;
private void ReturnToDisplayInterface()
{
// Reset the control values in the inserting interface
Suppliers.SelectedIndex = 0;
Categories.SelectedIndex = 0;
for (int i = firstControlID; i <= lastControlID; i++)
{
((TextBox)InsertingInterface.FindControl("ProductName" + i.ToString())).Text =
string.Empty;
((TextBox)InsertingInterface.FindControl("UnitPrice" + i.ToString())).Text =
string.Empty;
}
DisplayInterface.Visible = true;
InsertingInterface.Visible = false;
}
Оба 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
метод, который был создан еще в руководстве по переносу изменений базы данных в рамках транзакции , передает ProductsDataTable
ProductsTableAdapter
в метод s UpdateWithTransaction
. После этого запускается транзакция ADO.NET, и TableAdapter выдает инструкцию INSERT
в базу данных для каждого добавленного ProductsRow
в таблицу DataTable. При условии, что все продукты добавляются без ошибок, транзакция фиксируется, в противном случае выполняется откат.
Код для обработчика Click
событий Add Products from Shipment Button также должен выполнять проверку ошибок. Так как в интерфейсе вставки не используется requiredFieldValidators, пользователь может ввести цену на продукт, опустив его имя. Так как название продукта является обязательным, в случае развертывания такого условия необходимо предупредить пользователя и не продолжать вставку. Полный Click
код обработчика событий выглядит следующим образом:
protected void AddProducts_Click(object sender, EventArgs e)
{
// Make sure that the UnitPrice CompareValidators report valid data...
if (!Page.IsValid)
return;
// Add new ProductsRows to a ProductsDataTable...
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
for (int i = firstControlID; i <= lastControlID; i++)
{
// Read in the values for the product name and unit price
string productName = ((TextBox)InsertingInterface.FindControl
("ProductName" + i.ToString())).Text.Trim();
string unitPrice = ((TextBox)InsertingInterface.FindControl
("UnitPrice" + i.ToString())).Text.Trim();
// Ensure that if unitPrice has a value, so does productName
if (unitPrice.Length > 0 && productName.Length == 0)
{
// 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;
return;
}
// Only add the product if a product name value is provided
if (productName.Length > 0)
{
// Add a new ProductsRow to the ProductsDataTable
Northwind.ProductsRow newProduct = 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)
newProduct.UnitPrice = Convert.ToDecimal(unitPrice);
// Add any "default" values
newProduct.Discontinued = false;
newProduct.UnitsOnOrder = 0;
products.AddProductsRow(newProduct);
}
}
// If we reach here, see if there were any products added
if (products.Count > 0)
{
// Add the new products to the database using a transaction
ProductsBLL productsAPI = 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;
}
}
Обработчик событий начинается с того, что Page.IsValid
свойство возвращает значение true
. Если возвращается false
значение , это означает, что один или несколько объектов CompareValidators сообщают недопустимые данные. В этом случае мы не хотим пытаться вставить введенные продукты, иначе при попытке назначить введенное пользователем значение цены за единицу свойству ProductsRow
s UnitPrice
в конечном итоге возникнет исключение.
Затем создается новый ProductsDataTable
экземпляр (products
). Цикл for
используется для итерации по названию продукта и цене за единицу TextBoxes, а Text
свойства считываются в локальные переменные productName
и unitPrice
. Если пользователь ввел значение для цены за единицу, но не для соответствующего названия продукта, StatusLabel
отображается сообщение Если вы указали цену за единицу, необходимо также указать имя продукта, и обработчик событий будет завершен.
Если указано название продукта, создается новый ProductsRow
экземпляр с помощью ProductsDataTable
метода s NewProductsRow
. Этому новому ProductsRow
свойству экземпляра ProductName
присваивается текущее имя продукта TextBox, а SupplierID
свойства и CategoryID
— SelectedValue
свойствам 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. При вводе цены за единицу требуется название продукта (щелкните для просмотра полноразмерного изображения)
Рис. 14. Три новых veggies были добавлены для поставщика Mayumi (щелкните для просмотра полноразмерного изображения)
Рис. 15. Новые продукты можно найти на последней странице GridView (щелкните для просмотра полноразмерного изображения)
Примечание
Логика пакетной вставки, используемая в этом руководстве, заключает вставки в область транзакции. Чтобы проверить это, намеренно введите ошибку на уровне базы данных. Например, вместо назначения свойства нового ProductsRow
экземпляра CategoryID
выбранному значению в Categories
Раскрывающемся списке, назначьте его значению, например 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.