Partilhar via


Executar atualizações em lote (C#)

por Scott Mitchell

Baixar PDF

Saiba como criar um DataList totalmente editável em que todos os seus itens estão no modo de edição e cujos valores podem ser salvos clicando em um botão "Atualizar Tudo" na página.

Introdução

No tutorial anterior , examinamos como criar um DataList no nível do item. Assim como o GridView editável padrão, cada item na DataList incluía um botão Editar que, quando clicado, tornava o item editável. Embora essa edição em nível de item funcione bem para dados que são atualizados apenas ocasionalmente, determinados cenários de caso de uso exigem que o usuário edite muitos registros. Se um usuário precisar editar dezenas de registros e for forçado a clicar em Editar, fazer suas alterações e clicar em Atualizar para cada um deles, a quantidade de cliques poderá dificultar sua produtividade. Nessas situações, uma opção melhor é fornecer um DataList totalmente editável, em que todos os seus itens estão no modo de edição e cujos valores podem ser editados clicando em um botão Atualizar Tudo na página (consulte a Figura 1).

Cada item em um DataList totalmente editável pode ser modificado

Figura 1: cada item em um DataList totalmente editável pode ser modificado (clique para exibir a imagem em tamanho real)

Neste tutorial, examinaremos como permitir que os usuários atualizem informações de endereço de fornecedores usando uma DataList totalmente editável.

Etapa 1: Criar a interface do usuário editável no ItemTemplate da DataList

No tutorial anterior, em que criamos um DataList editável padrão no nível do item, usamos dois modelos:

  • ItemTemplate continha a interface do usuário somente leitura (os controles Da Web de Rótulo para exibir o nome e o preço de cada produto).
  • EditItemTemplate continha a interface do usuário do modo de edição (os dois controles da Web TextBox).

A propriedade DataList determina EditItemIndex o que DataListItem (se houver) é renderizado usando o EditItemTemplate. Em particular, o DataListItem cujo ItemIndex valor corresponde à propriedade DataList é EditItemIndex renderizado usando o EditItemTemplate. Esse modelo funciona bem quando apenas um item pode ser editado por vez, mas se desfaz ao criar um DataList totalmente editável.

Para um DataList totalmente editável, queremos que todos osDataListItem s sejam renderizados usando a interface editável. A maneira mais simples de fazer isso é definir a interface editável no ItemTemplate. Para modificar as informações de endereço dos fornecedores, a interface editável contém o nome do fornecedor como texto e, em seguida, TextBoxes para os valores de endereço, cidade e país/região.

Comece abrindo a BatchUpdate.aspx página, adicione um controle DataList e defina sua propriedade como SuppliersID . Na marca inteligente DataList, opte por adicionar um novo controle ObjectDataSource chamado SuppliersDataSource.

Criar um novo objectDataSource chamado SuppliersDataSource

Figura 2: Criar um novo objectDataSource chamado SuppliersDataSource (clique para exibir a imagem em tamanho real)

Configure o ObjectDataSource para recuperar dados usando o SuppliersBLL método da classe s GetSuppliers() (consulte a Figura 3). Assim como no tutorial anterior, em vez de atualizar as informações do fornecedor por meio do ObjectDataSource, trabalharemos diretamente com a Camada de Lógica de Negócios. Portanto, defina a lista suspensa como (Nenhum) na guia UPDATE (consulte a Figura 4).

Recuperar informações do fornecedor usando o método GetSuppliers()

Figura 3: Recuperar informações do fornecedor usando o GetSuppliers() método (clique para exibir a imagem em tamanho real)

Definir a lista de Drop-Down como (Nenhum) na guia UPDATE

Figura 4: Definir a lista de Drop-Down como (Nenhum) na guia UPDATE (clique para exibir a imagem em tamanho real)

Depois de concluir o assistente, o Visual Studio gera automaticamente os DataLists ItemTemplate para exibir cada campo de dados retornado pela fonte de dados em um controle Web Label. Precisamos modificar esse modelo para que ele forneça a interface de edição. O ItemTemplate pode ser personalizado por meio do Designer usando a opção Editar Modelos da marca inteligente DataList ou diretamente por meio da sintaxe declarativa.

Reserve um momento para criar uma interface de edição que exibe o nome do fornecedor como texto, mas inclui TextBoxes para os valores de endereço, cidade e país/região do fornecedor. Depois de fazer essas alterações, a sintaxe declarativa da página deve ser semelhante à seguinte:

<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>

Observação

Assim como no tutorial anterior, o DataList neste tutorial deve ter seu estado de exibição habilitado.

ItemTemplate No , estou usando duas novas classes CSS, SupplierPropertyLabel e SupplierPropertyValue, que foram adicionadas à Styles.css classe e configuradas para usar as mesmas configurações de estilo que as ProductPropertyLabel classes CSS e ProductPropertyValue .

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

Depois de fazer essas alterações, visite esta página por meio de um navegador. Como mostra a Figura 5, cada item DataList exibe o nome do fornecedor como texto e usa TextBoxes para exibir o endereço, a cidade e o país/região.

Cada fornecedor na DataList é editável

Figura 5: Cada fornecedor na DataList é editável (clique para exibir a imagem em tamanho real)

Etapa 2: Adicionar um botão Atualizar Tudo

Embora cada fornecedor na Figura 5 tenha seus campos de endereço, cidade e país/região exibidos em um TextBox, atualmente não há nenhum botão Atualizar disponível. Em vez de ter um botão Atualizar por item, com DataLists totalmente editáveis, normalmente há um único botão Atualizar Tudo na página que, quando clicado, atualiza todos os registros na DataList. Para este tutorial, vamos adicionar dois botões Atualizar Tudo – um na parte superior da página e outro na parte inferior (embora clicar em um dos botões tenha o mesmo efeito).

Comece adicionando um controle web de botão acima da DataList e defina sua ID propriedade como UpdateAll1. Em seguida, adicione o segundo controle Web button abaixo de DataList, definindo-o IDUpdateAll2como . Defina as Text propriedades dos dois Botões como Atualizar Tudo. Por fim, crie manipuladores de eventos para ambos os eventos Buttons Click . Em vez de duplicar a lógica de atualização em cada um dos manipuladores de eventos, vamos refatorar essa lógica para um terceiro método, UpdateAllSupplierAddresses, tendo os manipuladores de eventos simplesmente invocando esse terceiro método.

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
}

A Figura 6 mostra a página depois que os botões Atualizar Tudo tiverem sido adicionados.

Dois botões Atualizar Todos foram adicionados à página

Figura 6: Dois botões Atualizar Todos foram adicionados à página (clique para exibir a imagem em tamanho real)

Etapa 3: Atualizando todas as informações de endereço de fornecedores

Com todos os itens datalist exibindo a interface de edição e com a adição dos botões Atualizar Tudo, tudo o que resta é escrever o código para executar a atualização em lote. Especificamente, precisamos executar um loop pelos itens de DataList e chamar o SuppliersBLL método da classe s UpdateSupplierAddress para cada um deles.

A coleção de DataListItem instâncias que compõem o DataList pode ser acessada por meio da propriedade DataList.Items Com uma referência a um DataListItem, podemos pegar o correspondente SupplierID da DataKeys coleção e referenciar programaticamente os controles Da Web TextBox dentro do ItemTemplate , como ilustra o código a seguir:

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);
    }
}

Quando o usuário clica em um dos botões Atualizar Tudo, o UpdateAllSupplierAddresses método itera por cada DataListItem um na Suppliers DataList e chama o SuppliersBLL método da classe s UpdateSupplierAddress , passando os valores correspondentes. Um valor não inserido para passagens de endereço, cidade ou país/região é um valor de para UpdateSupplierAddress (em vez de Nothing uma cadeia de caracteres em branco), o que resulta em um banco de dados NULL para os campos s de registro subjacentes.

Observação

Como um aprimoramento, talvez você queira adicionar um controle Web status Label à página que fornece alguma mensagem de confirmação após a atualização em lote ser executada.

Atualizando apenas os endereços que foram modificados

O algoritmo de atualização em lote usado para este tutorial chama o UpdateSupplierAddress método para cada fornecedor na DataList, independentemente de suas informações de endereço serem alteradas. Embora essas atualizações cegas geralmente não sejam um problema de desempenho, elas podem levar a registros supérfluos se você estiver auditando alterações na tabela de banco de dados. Por exemplo, se você usar gatilhos para registrar todos os UPDATE s Suppliers na tabela em uma tabela de auditoria, sempre que um usuário clicar no botão Atualizar Tudo, um novo registro de auditoria será criado para cada fornecedor no sistema, independentemente de o usuário ter feito alterações.

As classes ADO.NET DataTable e DataAdapter foram projetadas para dar suporte a atualizações em lote em que apenas registros modificados, excluídos e novos resultam em qualquer comunicação de banco de dados. Cada linha na DataTable tem uma RowState propriedade que indica se a linha foi adicionada à DataTable, excluída dela, modificada ou permanece inalterada. Quando um DataTable é preenchido inicialmente, todas as linhas são marcadas como inalteradas. Alterar o valor de qualquer uma das colunas da linha marca a linha como modificada.

SuppliersBLL Na classe , atualizamos as informações de endereço do fornecedor especificadas primeiro lendo no registro de fornecedor único em um SuppliersDataTable e, em seguida, definimos os Addressvalores de coluna , Citye Country usando o seguinte código:

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;
    }
}

Esse código atribui com ingenuidade os valores de endereço, cidade e país/região passados ao SuppliersRow , SuppliersDataTable independentemente de os valores terem sido alterados ou não. Essas modificações fazem com que a SuppliersRow propriedade s RowState seja marcada como modificada. Quando o método da Camada de Update Acesso a Dados é chamado, ele vê que o SupplierRow foi modificado e, portanto, envia um UPDATE comando para o banco de dados.

Imagine, no entanto, que adicionamos código a esse método para atribuir apenas os valores de endereço, cidade e país/região passados se eles forem diferentes dos SuppliersRow valores existentes. No caso em que o endereço, a cidade e o país/região são iguais aos dados existentes, nenhuma alteração será feita e os SupplierRow s RowState serão deixados marcados como inalterados. O resultado líquido é que, quando o método da DAL é Update chamado, nenhuma chamada de banco de dados será feita porque o SuppliersRow não foi modificado.

Para aplicar essa alteração, substitua as instruções que atribuem cegamente os valores de endereço, cidade e país/região passados pelo seguinte código:

// 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;

Com esse código adicionado, o método da DAL envia Update uma instrução UPDATE para o banco de dados somente para os registros cujos valores relacionados ao endereço foram alterados.

Como alternativa, podemos controlar se há diferenças entre os campos de endereço passados e os dados do banco de dados e, se não houver nenhum, simplesmente ignorar a chamada para o método da DAL.Update Essa abordagem funcionará bem se você estiver usando o método direto do BD, já que o método direto do BD não é passado para uma SuppliersRow instância cujo RowState pode ser verificado para determinar se uma chamada de banco de dados é realmente necessária.

Observação

Sempre que o UpdateSupplierAddress método é invocado, uma chamada é feita ao banco de dados para recuperar informações sobre o registro atualizado. Em seguida, se houver alterações nos dados, outra chamada para o banco de dados será feita para atualizar a linha da tabela. Esse fluxo de trabalho pode ser otimizado criando uma UpdateSupplierAddress sobrecarga de método que aceita uma EmployeesDataTable instância que tem todas as alterações da BatchUpdate.aspx página. Em seguida, ele pode fazer uma chamada para o banco de dados para obter todos os registros da Suppliers tabela. Os dois conjuntos de resultados poderiam então ser enumerados e apenas os registros em que ocorreram alterações poderiam ser atualizados.

Resumo

Neste tutorial, vimos como criar um DataList totalmente editável, permitindo que um usuário modifique rapidamente as informações de endereço para vários fornecedores. Começamos definindo a interface de edição um controle Da Web TextBox para os valores de endereço, cidade e país/região do fornecedor no DataList s ItemTemplate. Em seguida, adicionamos os botões Atualizar Todos acima e abaixo de DataList. Depois que um usuário fez suas alterações e clicou em um dos botões Atualizar Tudo, os DataListItem s são enumerados e uma chamada para o SuppliersBLL método da UpdateSupplierAddress classe é feita.

Programação feliz!

Sobre o autor

Scott Mitchell, autor de sete livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.

Agradecimentos Especiais

Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Zack Jones e Ken Pespisa. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.