Выполнение пакетных обновлений (C#)
Узнайте, как создать полностью редактируемый список данных, в котором все его элементы находятся в режиме редактирования и значения которого можно сохранить, нажав кнопку "Обновить все" на странице.
Введение
В предыдущем руководстве мы рассмотрели, как создать DataList на уровне элемента. Как и стандартный редактируемый GridView, каждый элемент в DataList включает кнопку Изменить, которая при нажатии делает элемент редактируемым. Хотя это редактирование на уровне элемента хорошо подходит для данных, которые обновляются только изредка, в некоторых сценариях использования требуется, чтобы пользователь редактировал множество записей. Если пользователю нужно изменить десятки записей и он вынужден нажать кнопку Изменить, внести изменения и нажать кнопку Обновить для каждой из них, количество щелчков может помешать его производительности. В таких ситуациях лучше предоставить полностью редактируемый список данных DataList, в котором все его элементы находятся в режиме редактирования и значения которого можно изменить, нажав кнопку Обновить все на странице (см. рис. 1).
Рис. 1. Каждый элемент в списке полностью редактируемых данных можно изменить (щелкните для просмотра полноразмерного изображения)
В этом руководстве мы рассмотрим, как разрешить пользователям обновлять адресную информацию о поставщиках с помощью полностью редактируемого списка данных.
Шаг 1. Создание редактируемого пользовательского интерфейса в ItemTemplate dataList
В предыдущем руководстве, где мы создавали стандартный редактируемый DataList на уровне элемента, мы использовали два шаблона:
ItemTemplate
содержит пользовательский интерфейс только для чтения (веб-элементы управления метки для отображения названия и цены каждого продукта).EditItemTemplate
содержит пользовательский интерфейс режима редактирования (два веб-элемента управления TextBox).
Свойство DataList EditItemIndex
определяет, что DataListItem
(при наличии) отображается с помощью EditItemTemplate
. В частности, объект , DataListItem
значение которого ItemIndex
соответствует свойству DataList, EditItemIndex
отображается с помощью EditItemTemplate
. Эта модель хорошо работает, когда за раз можно изменить только один элемент, но при создании полностью редактируемого DataList распадается.
Для полностью редактируемого dataList мы хотим, чтобы всеDataListItem
элементы отображались с помощью редактируемого интерфейса. Самый простой способ сделать это — определить редактируемый интерфейс в ItemTemplate
. Для изменения сведений об адресе поставщиков редактируемый интерфейс содержит имя поставщика в виде текста, а затем textBoxes для значений адреса, города и страны или региона.
Начните с BatchUpdate.aspx
открытия страницы, добавьте элемент управления DataList и задайте для его ID
свойства значение Suppliers
. В смарт-теге DataList выберите добавление нового элемента управления ObjectDataSource с именем SuppliersDataSource
.
Рис. 2. Создание объекта ObjectDataSource с именем SuppliersDataSource
(щелкните для просмотра полноразмерного изображения)
Настройте ObjectDataSource для получения данных с помощью SuppliersBLL
метода класса s GetSuppliers()
(см. рис. 3). Как и в предыдущем руководстве, вместо обновления сведений о поставщике с помощью ObjectDataSource мы будем работать непосредственно с уровнем бизнес-логики. Поэтому на вкладке ОБНОВЛЕНИЕ в раскрывающемся списке задайте значение (Нет) (см. рис. 4).
Рис. 3. Получение сведений о поставщике GetSuppliers()
с помощью метода (щелкните для просмотра полноразмерного изображения)
Рис. 4. Задайте для списка Drop-Down значение (Нет) на вкладке UPDATE (щелкните для просмотра полноразмерного изображения)
После завершения работы мастера Visual Studio автоматически создает dataList ItemTemplate
для отображения каждого поля данных, возвращаемого источником данных в веб-элементе управления Метка. Нам нужно изменить этот шаблон, чтобы он предоставлял интерфейс редактирования. Можно ItemTemplate
настроить с помощью Designer с помощью параметра Изменить шаблоны из смарт-тега DataList или непосредственно с помощью декларативного синтаксиса.
Уделите немного времени, чтобы создать интерфейс редактирования, который отображает имя поставщика в виде текста, но включает textBoxes для значений адреса, города и страны или региона поставщика. После внесения этих изменений декларативный синтаксис страницы должен выглядеть примерно так:
<asp:DataList ID="Suppliers" runat="server" DataKeyField="SupplierID"
DataSourceID="SuppliersDataSource">
<ItemTemplate>
<h4><asp:Label ID="CompanyNameLabel" runat="server"
Text='<%# Eval("CompanyName") %>' /></h4>
<table border="0">
<tr>
<td class="SupplierPropertyLabel">Address:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Address" runat="server"
Text='<%# Eval("Address") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">City:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="City" runat="server"
Text='<%# Eval("City") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">Country:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Country" runat="server"
Text='<%# Eval("Country") %>' />
</td>
</tr>
</table>
<br />
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>
Примечание
Как и в предыдущем руководстве, для DataList в этом руководстве должно быть включено состояние представления.
ItemTemplate
В I m используются два новых класса CSS и SupplierPropertyLabel
SupplierPropertyValue
, которые были добавлены в Styles.css
класс и настроены для использования параметров стиля, что ProductPropertyLabel
и ProductPropertyValue
классы CSS.
.ProductPropertyLabel, .SupplierPropertyLabel
{
font-weight: bold;
text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
padding-right: 35px;
}
После внесения этих изменений перейдите на эту страницу через браузер. Как показано на рисунке 5, каждый элемент DataList отображает имя поставщика в виде текста и использует TextBoxes для отображения адреса, города и страны или региона.
Рис. 5. Каждый поставщик в списке данных доступен для редактирования (щелкните для просмотра полноразмерного изображения)
Шаг 2. Добавление кнопки "Обновить все"
Хотя у каждого поставщика на рис. 5 в поле TextBox отображаются поля адреса, города и страны или региона, в настоящее время кнопка "Обновить" недоступна. Вместо того чтобы иметь кнопку "Обновить" для каждого элемента, при использовании полностью редактируемых списков данных на странице обычно есть одна кнопка "Обновить все", которая при нажатии обновляет все записи в Списке данных. В этом руководстве мы добавим две кнопки "Обновить все" — одну в верхней части страницы, а другую внизу (хотя нажатие любой кнопки будет иметь одинаковый эффект).
Начните с добавления веб-элемента управления Button над DataList и задайте для его ID
свойства значение UpdateAll1
. Затем добавьте второй веб-элемент управления Кнопка под DataList, задав для этого ID
элемента значение UpdateAll2
. Text
Задайте свойства для двух кнопок, чтобы обновить все. Наконец, создайте обработчики событий для обоих событий Button Click
. Вместо того чтобы дублировать логику обновления в каждом обработчике событий, позвольте выполнить рефакторинг этой логики в третий метод , UpdateAllSupplierAddresses
чтобы обработчики событий просто вызвали этот третий метод.
protected void UpdateAll1_Click(object sender, EventArgs e)
{
UpdateAllSupplierAddresses();
}
protected void UpdateAll2_Click(object sender, EventArgs e)
{
UpdateAllSupplierAddresses();
}
private void UpdateAllSupplierAddresses()
{
// TODO: Write code to update _all_ of the supplier addresses in the DataList
}
На рисунке 6 показана страница после добавления кнопок Обновить все.
Рис. 6. На страницу добавлены две кнопки "Обновить все" (щелкните для просмотра полноразмерного изображения)
Шаг 3. Обновление всех сведений об адресе поставщиков
При отображении интерфейса редактирования всех элементов DataList и добавлении кнопок Обновить все остается только написание кода для выполнения пакетного обновления. В частности, необходимо выполнить циклическую обработку элементов DataList и вызвать SuppliersBLL
метод класса UpdateSupplierAddress
для каждого из них.
Доступ к коллекции экземпляров DataListItem
DataList можно получить с помощью свойства DataListItems
. Используя ссылку на , мы можем получить соответствующий DataListItem
SupplierID
объект из DataKeys
коллекции и программно ссылаться на веб-элементы управления TextBox в , ItemTemplate
как показано в следующем коде:
private void UpdateAllSupplierAddresses()
{
// Create an instance of the SuppliersBLL class
SuppliersBLL suppliersAPI = new SuppliersBLL();
// Iterate through the DataList's items
foreach (DataListItem item in Suppliers.Items)
{
// Get the supplierID from the DataKeys collection
int supplierID = Convert.ToInt32(Suppliers.DataKeys[item.ItemIndex]);
// Read in the user-entered values
TextBox address = (TextBox)item.FindControl("Address");
TextBox city = (TextBox)item.FindControl("City");
TextBox country = (TextBox)item.FindControl("Country");
string addressValue = null, cityValue = null, countryValue = null;
if (address.Text.Trim().Length > 0)
addressValue = address.Text.Trim();
if (city.Text.Trim().Length > 0)
cityValue = city.Text.Trim();
if (country.Text.Trim().Length > 0)
countryValue = country.Text.Trim();
// Call the SuppliersBLL class's UpdateSupplierAddress method
suppliersAPI.UpdateSupplierAddress
(supplierID, addressValue, cityValue, countryValue);
}
}
Когда пользователь нажимает одну из кнопок Обновить все, UpdateAllSupplierAddresses
метод выполняет итерацию по каждому Suppliers
из них DataListItem
в DataList и вызывает SuppliersBLL
метод класса sUpdateSupplierAddress
, передавая соответствующие значения. Невводимым значением для адресов, городов или стран или регионов является значение Nothing
для UpdateSupplierAddress
(а не пустая строка), что приводит к базе данных NULL
для полей базовых записей.
Примечание
В качестве улучшения может потребоваться добавить веб-элемент управления Метка состояния на страницу, которая предоставляет сообщение с подтверждением после выполнения пакетного обновления.
Обновление только тех адресов, которые были изменены
Алгоритм пакетного обновления, используемый в этом руководстве UpdateSupplierAddress
, вызывает метод для каждого поставщика в DataList, независимо от того, были ли изменены сведения об адресе. Хотя такие слепые обновления обычно не являются проблемой производительности, они могут привести к излишним записям при повторном аудите изменений в таблице базы данных. Например, если вы используете триггеры для записи всех UPDATE
элементов в таблицу Suppliers
аудита, каждый раз, когда пользователь нажимает кнопку Обновить все, для каждого поставщика в системе будет создаваться новая запись аудита, независимо от того, вносил ли пользователь какие-либо изменения.
Классы ADO.NET DataTable и DataAdapter предназначены для поддержки пакетных обновлений, когда только измененные, удаленные и новые записи приводят к обмену данными с базами данных. Каждая строка в таблице DataTable имеет RowState
свойство , указывающее, была ли строка добавлена в DataTable, удалена из нее, изменена или остается неизменной. При первоначальном заполнении таблицы Данных все строки помечаются без изменений. Изменение значения любого столбца строки помечает строку как измененную.
SuppliersBLL
В классе мы обновляем сведения об адресе указанного поставщика, сначала считывая запись одного поставщика в , SuppliersDataTable
а затем задаем Address
значения столбцов , City
и Country
с помощью следующего кода:
public bool UpdateSupplierAddress
(int supplierID, string address, string city, string country)
{
Northwind.SuppliersDataTable suppliers =
Adapter.GetSupplierBySupplierID(supplierID);
if (suppliers.Count == 0)
// no matching record found, return false
return false;
else
{
Northwind.SuppliersRow supplier = suppliers[0];
if (address == null)
supplier.SetAddressNull();
else
supplier.Address = address;
if (city == null)
supplier.SetCityNull();
else
supplier.City = city;
if (country == null)
supplier.SetCountryNull();
else
supplier.Country = country;
// Update the supplier Address-related information
int rowsAffected = Adapter.Update(supplier);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
}
Этот код наивно назначает переданные значения SuppliersRow
адреса, города и страны или региона в в SuppliersDataTable
независимо от того, изменились ли значения. Эти изменения приводят к тому SuppliersRow
, что свойство s RowState
помечается как измененное. При вызове метода уровня Update
доступа к данным он видит, что SupplierRow
объект был изменен, и, следовательно, отправляет UPDATE
команду в базу данных.
Однако представьте, что мы добавили в этот метод код, чтобы назначить переданные значения адреса, города и страны или региона только в том случае, если они отличаются от SuppliersRow
существующих значений. В случае, когда адрес, город и страна или регион совпадают с существующими данными, изменения не будут вноситься, и SupplierRow
RowState
они будут помечены как без изменений. В результате при вызове метода DAL Update
вызов базы данных не будет выполнен, так как SuppliersRow
не был изменен.
Чтобы принять это изменение, замените инструкции, которые слепо назначают переданные значения адреса, города и страны или региона, следующим кодом:
// Only assign the values to the SupplierRow's column values if they differ
if (address == null && !supplier.IsAddressNull())
supplier.SetAddressNull();
else if ((address != null && supplier.IsAddressNull()) ||
(!supplier.IsAddressNull() &&
string.Compare(supplier.Address, address) != 0))
supplier.Address = address;
if (city == null && !supplier.IsCityNull())
supplier.SetCityNull();
else if ((city != null && supplier.IsCityNull()) ||
(!supplier.IsCityNull() && string.Compare(supplier.City, city) != 0))
supplier.City = city;
if (country == null && !supplier.IsCountryNull())
supplier.SetCountryNull();
else if ((country != null && supplier.IsCountryNull()) ||
(!supplier.IsCountryNull() &&
string.Compare(supplier.Country, country) != 0))
supplier.Country = country;
С помощью этого добавленного кода метод DAL Update
отправляет инструкцию UPDATE
в базу данных только для тех записей, значения, связанные с адресом которых были изменены.
Кроме того, можно отслеживать, есть ли различия между передаваемыми полями адресов и данными базы данных, и, если их нет, просто обойти вызов метода DAL Update
. Этот подход хорошо работает, если вы используете прямой метод базы данных, так как прямой метод базы данных не передается экземпляру SuppliersRow
, который RowState
можно проверить, чтобы определить, требуется ли вызов базы данных на самом деле.
Примечание
При каждом вызове UpdateSupplierAddress
метода выполняется вызов базы данных для получения сведений об обновленной записи. Затем, если в данных есть какие-либо изменения, выполняется еще один вызов базы данных для обновления строки таблицы. Этот рабочий процесс можно оптимизировать, создав перегрузку UpdateSupplierAddress
метода, которая принимает EmployeesDataTable
экземпляр со всеми изменениями со BatchUpdate.aspx
страницы. Затем он может выполнить один вызов к базе данных, чтобы получить все записи из Suppliers
таблицы. Затем можно перечислить два набора результатов и обновить только те записи, в которых произошли изменения.
Сводка
В этом руководстве мы узнали, как создать полностью редактируемый DataList, позволяющий пользователю быстро изменять сведения об адресах для нескольких поставщиков. Мы начали с определения интерфейса редактирования веб-элемента управления TextBox для значений адреса, города и страны или региона поставщика в DataList ItemTemplate
. Далее мы добавили кнопки Обновить все над и под DataList. После того как пользователь внес свои изменения и нажал одну из кнопок Обновить все, DataListItem
выполняется перечисление и вызов SuppliersBLL
метода класса s UpdateSupplierAddress
.
Счастливое программирование!
Об авторе
Скотт Митчелл (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.