Выполнение пакетных обновлений (C#)

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

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

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

Введение

В предыдущем руководстве мы рассмотрели, как создать 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.

Создание объекта ObjectDataSource с именем SuppliersDataSource

Рис. 2. Создание объекта ObjectDataSource с именем SuppliersDataSource (щелкните для просмотра полноразмерного изображения)

Настройте ObjectDataSource для получения данных с помощью SuppliersBLL метода класса s GetSuppliers() (см. рис. 3). Как и в предыдущем руководстве, вместо обновления сведений о поставщике с помощью ObjectDataSource мы будем работать непосредственно с уровнем бизнес-логики. Поэтому на вкладке ОБНОВЛЕНИЕ в раскрывающемся списке задайте значение (Нет) (см. рис. 4).

Получение сведений о поставщике с помощью метода GetSuppliers()

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

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

Рис. 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 и SupplierPropertyLabelSupplierPropertyValue, которые были добавлены в Styles.css класс и настроены для использования параметров стиля, что ProductPropertyLabel и ProductPropertyValue классы CSS.

.ProductPropertyLabel, .SupplierPropertyLabel
{
    font-weight: bold;
    text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
    padding-right: 35px;
}

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

Каждый поставщик в DataList является редактируемым

Рис. 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. Используя ссылку на , мы можем получить соответствующий DataListItemSupplierID объект из 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 существующих значений. В случае, когда адрес, город и страна или регион совпадают с существующими данными, изменения не будут вноситься, и SupplierRowRowState они будут помечены как без изменений. В результате при вызове метода 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.