Atualização em lote (C#)

por Scott Mitchell

Baixar PDF

Saiba como atualizar vários registros de banco de dados em uma única operação. Na Camada de Interface do Usuário, criamos um GridView em que cada linha é editável. Na Camada de Acesso a Dados, encapsulamos as várias operações de Atualização dentro de uma transação para garantir que todas as atualizações sejam bem-sucedidas ou todas as atualizações sejam revertidas.

Introdução

No tutorial anterior , vimos como estender a Camada de Acesso a Dados para adicionar suporte a transações de banco de dados. As transações de banco de dados garantem que uma série de instruções de modificação de dados será tratada como uma operação atômica, o que garante que todas as modificações falharão ou todas terão êxito. Com essa funcionalidade de DAL de baixo nível fora do caminho, estamos prontos para voltar nossa atenção para a criação de interfaces de modificação de dados em lote.

Neste tutorial, criaremos um GridView em que cada linha é editável (consulte a Figura 1). Como cada linha é renderizada em sua interface de edição, não há necessidade de uma coluna dos botões Editar, Atualizar e Cancelar. Em vez disso, há dois botões Atualizar Produtos na página que, quando clicados, enumeram as linhas GridView e atualizam o banco de dados.

Cada linha no GridView é editável

Figura 1: cada linha no GridView é editável (clique para exibir a imagem em tamanho real)

Vamos começar!

Observação

No tutorial Executando o Lote Atualizações criamos uma interface de edição em lote usando o controle DataList. Este tutorial difere do anterior em que usa um GridView e a atualização em lote é executada dentro do escopo de uma transação. Depois de concluir este tutorial, encorajo você a retornar ao tutorial anterior e atualizá-lo para usar a funcionalidade relacionada à transação de banco de dados adicionada no tutorial anterior.

Examinando as etapas para tornar todas as linhas gridView editáveis

Conforme discutido no tutorial Uma visão geral de inserção, atualização e exclusão de dados , o GridView oferece suporte interno para editar seus dados subjacentes por linha. Internamente, GridView observa qual linha é editável por meio de sua EditIndex propriedade. Como o GridView está sendo associado à sua fonte de dados, ele verifica cada linha para ver se o índice da linha é igual ao valor de EditIndex. Nesse caso, esses campos de linhas são renderizados usando suas interfaces de edição. Para BoundFields, a interface de edição é uma TextBox cuja Text propriedade é atribuída ao valor do campo de dados especificado pela propriedade BoundField s DataField . Para TemplateFields, o EditItemTemplate é usado no lugar do ItemTemplate.

Lembre-se de que o fluxo de trabalho de edição é iniciado quando um usuário clica no botão Editar de uma linha. Isso causa um postback, define a propriedade gridView como EditIndex o índice de linhas clicadas e reassocia os dados à grade. Quando um botão Cancelar de uma linha é clicado, no postback o EditIndex é definido como um valor de antes de -1 reassociar os dados à grade. Como as linhas do GridView começam a indexar em zero, a configuração como EditIndex-1 tem o efeito de exibir o GridView no modo somente leitura.

A EditIndex propriedade funciona bem para edição por linha, mas não foi projetada para edição em lote. Para tornar todo o GridView editável, precisamos ter cada linha renderizada usando sua interface de edição. A maneira mais fácil de fazer isso é criar onde cada campo editável é implementado como um TemplateField com sua interface de edição definida no ItemTemplate.

Nas próximas etapas, criaremos um GridView completamente editável. Na Etapa 1, começaremos criando o GridView e seu ObjectDataSource e convertendo boundFields e CheckBoxField em TemplateFields. Nas Etapas 2 e 3, moveremos as interfaces de edição dos TemplateFields EditItemTemplate para seus ItemTemplate s.

Etapa 1: Exibindo informações do produto

Antes de nos preocuparmos em criar um GridView em que as linhas são editáveis, vamos começar simplesmente exibindo as informações do produto. Abra a BatchUpdate.aspx página na BatchData pasta e arraste um GridView da Caixa de Ferramentas para o Designer. Defina o GridView s ID como ProductsGrid e, de sua marca inteligente, escolha associá-lo a um novo ObjectDataSource chamado ProductsDataSource. Configure o ObjectDataSource para recuperar seus dados do ProductsBLL método da classe s GetProducts .

Configurar o ObjectDataSource para usar a classe ProductsBLL

Figura 2: Configurar o ObjectDataSource para usar a ProductsBLL classe (clique para exibir a imagem em tamanho real)

Recuperar os dados do produto usando o método GetProducts

Figura 3: Recuperar os dados do produto usando o GetProducts método (clique para exibir a imagem em tamanho real)

Assim como o GridView, os recursos de modificação do ObjectDataSource são projetados para funcionar por linha. Para atualizar um conjunto de registros, precisaremos escrever um pouco de código na classe code-behind da página ASP.NET que agrupa os dados e os passa para a BLL. Portanto, defina as listas suspensas nas guias UPDATE, INSERT e DELETE do ObjectDataSource como (Nenhum). Clique em Concluir para concluir o assistente.

Defina o Drop-Down Listas nas guias UPDATE, INSERT e DELETE como (Nenhum)

Figura 4: Definir o Drop-Down Listas nas guias UPDATE, INSERT e DELETE como (Nenhum) (Clique para exibir a imagem em tamanho real)

Depois de concluir o assistente Configurar Fonte de Dados, a marcação declarativa do ObjectDataSource deverá ser semelhante à seguinte:

<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>

A conclusão do assistente Configurar Fonte de Dados também faz com que o Visual Studio crie BoundFields e um CheckBoxField para os campos de dados do produto no GridView. Para este tutorial, vamos permitir apenas que o usuário exiba e edite o nome, a categoria, o preço e os status descontinuados do produto. Remova todos, exceto os ProductNamecampos , CategoryName, UnitPricee Discontinued e renomeie as HeaderText propriedades dos três primeiros campos como Produto, Categoria e Preço, respectivamente. Por fim, marcar as caixas de seleção Habilitar Paginação e Habilitar Classificação na marca inteligente GridView.

Neste ponto, o GridView tem três BoundFields (ProductName, CategoryNamee UnitPrice) e um CheckBoxField (Discontinued). Precisamos converter esses quatro campos em TemplateFields e, em seguida, mover a interface de edição do TemplateField s EditItemTemplate para seu ItemTemplate.

Observação

Exploramos a criação e a personalização de TemplateFields no tutorial Personalizando a Interface de Modificação de Dados . Vamos percorrer as etapas de conversão de BoundFields e CheckBoxField em TemplateFields e definição de suas interfaces de edição em seus ItemTemplate s, mas se você ficar preso ou precisar de um atualizador, não hesite em fazer referência a este tutorial anterior.

Na marca inteligente GridView, clique no link Editar Colunas para abrir a caixa de diálogo Campos. Em seguida, selecione cada campo e clique no link Converter este campo em um TemplateField.

Converter BoundFields e CheckBoxField existentes em TemplateField

Figura 5: Converter BoundFields e CheckBoxField existentes em TemplateField

Agora que cada campo é um TemplateField, estamos prontos para mover a interface de edição dos EditItemTemplate s para os ItemTemplate s.

Etapa 2: Criando asProductNameUnitPrice interfaces de edição eDiscontinued

Criar as ProductNameinterfaces de edição , UnitPricee Discontinued são o tópico desta etapa e são bastante simples, pois cada interface já está definida no TemplateField s EditItemTemplate. A criação da CategoryName interface de edição é um pouco mais envolvida, pois precisamos criar uma DropDownList das categorias aplicáveis. Essa CategoryName interface de edição é abordada na Etapa 3.

Vamos começar com TemplateField ProductName . Clique no link Editar Modelos da marca inteligente GridView e faça drill down até TemplateField ProductName s EditItemTemplate. Selecione TextBox, copie-o para a área de transferência e cole-o no ProductName TemplateField s ItemTemplate. Altere a propriedade textBox s ID para ProductName.

Em seguida, adicione um RequiredFieldValidator ao ItemTemplate para garantir que o usuário forneça um valor para cada nome de produto. Defina a ControlToValidate propriedade como ProductName, a ErrorMessage propriedade como Você deve fornecer o nome do produto. e a Text propriedade para *. Depois de fazer essas adições ao ItemTemplate, sua tela deve ser semelhante à Figura 6.

O ProductName TemplateField agora inclui uma TextBox e um RequiredFieldValidator

Figura 6: O ProductName TemplateField agora inclui uma TextBox e um RequiredFieldValidator (clique para exibir a imagem em tamanho real)

Para a UnitPrice interface de edição, comece copiando TextBox do EditItemTemplate para o ItemTemplate. Em seguida, coloque um $ na frente da TextBox e defina sua ID propriedade como UnitPrice e sua Columns propriedade como 8 .

Adicione também um CompareValidator aos UnitPrice s ItemTemplate para garantir que o valor inserido pelo usuário seja um valor de moeda válido maior ou igual a US$ 0,00. Defina a propriedade do validador como ControlToValidate UnitPrice, sua propriedade como ErrorMessage Você deve inserir um valor de moeda válido. Omita todos os símbolos de moeda, sua Text propriedade para *, sua Type propriedade para Currency, sua Operator propriedade para GreaterThanEquale sua ValueToCompare propriedade como 0 .

Adicionar um CompareValidator para garantir que o preço inserido seja um valor de Conversor de Moedas não negativo

Figura 7: Adicionar um CompareValidator para garantir que o preço inserido seja um valor de Conversor de Moedas não negativo (clique para exibir a imagem em tamanho real)

Para o Discontinued TemplateField, você pode usar a CheckBox já definida no ItemTemplate. Basta definir como ID Descontinuado e sua Enabled propriedade como true.

Etapa 3: Criando aCategoryNameinterface de edição

A interface de edição no CategoryName TemplateField s EditItemTemplate contém um TextBox que exibe o valor do campo de CategoryName dados. Precisamos substituir isso por um DropDownList que lista as categorias possíveis.

Observação

O tutorial Personalizando a Interface de Modificação de Dados contém uma discussão mais completa e completa sobre como personalizar um modelo para incluir um DropDownList em vez de um TextBox. Embora as etapas aqui estejam concluídas, elas são apresentadas com tersely. Para obter uma visão mais detalhada sobre como criar e configurar as categorias DropDownList, consulte o tutorial Personalizando a Interface de Modificação de Dados .

Arraste um DropDownList da Caixa de Ferramentas para o TemplateField s , definindo-o CategoryNameID como Categories.ItemTemplate Neste ponto, normalmente definiríamos a fonte de dados do DropDownLists por meio de sua marca inteligente, criando um novo ObjectDataSource. No entanto, isso adicionará ObjectDataSource no , o ItemTemplateque resultará em uma instância ObjectDataSource criada para cada linha GridView. Em vez disso, vamos criar o ObjectDataSource fora dos TemplateFields do GridView. Encerre a edição do modelo e arraste um ObjectDataSource da Caixa de Ferramentas para a Designer abaixo do ProductsDataSource ObjectDataSource. Nomeie o novo ObjectDataSource CategoriesDataSource e configure-o para usar o CategoriesBLL método da classe s GetCategories .

Configurar o ObjectDataSource para usar a classe CategoriesBLL

Figura 8: Configurar o ObjectDataSource para usar a CategoriesBLL classe (Clique para exibir a imagem em tamanho real)

Recuperar os dados de categoria usando o método GetCategories

Figura 9: recuperar os dados de categoria usando o GetCategories método (clique para exibir a imagem em tamanho real)

Como este ObjectDataSource é usado apenas para recuperar dados, defina as listas suspensas nas guias UPDATE e DELETE como (Nenhum). Clique em Concluir para concluir o assistente.

Defina o Drop-Down Listas nas guias UPDATE e DELETE como (Nenhum)

Figura 10: defina o Drop-Down Listas nas guias UPDATE e DELETE como (Nenhum) (Clique para exibir a imagem em tamanho real)

Depois de concluir o assistente, a CategoriesDataSource marcação declarativa do s deve ser semelhante à seguinte:

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Com o CategoriesDataSource criado e configurado, retorne ao CategoryName TemplateField s ItemTemplate e, na marca inteligente DropDownList, clique no link Escolher Fonte de Dados. No assistente Configuração da Fonte de Dados, selecione a opção CategoriesDataSource na primeira lista suspensa e escolha ter CategoryName usado para a exibição e CategoryID como o valor.

Associar o DropDownList às CategoriasDataSource

Figura 11: Associar o DropDownList ao CategoriesDataSource (Clique para exibir a imagem em tamanho real)

Neste ponto, o Categories DropDownList lista todas as categorias, mas ainda não seleciona automaticamente a categoria apropriada para o produto associado à linha GridView. Para fazer isso, precisamos definir o Categories DropDownList s SelectedValue para o valor do CategoryID produto. Clique no link Editar DataBindings da marca inteligente DropDownList e associe a SelectedValue propriedade ao CategoryID campo de dados, conforme mostrado na Figura 12.

Associar o valor categoryID do produto à propriedade SelectedValue do DropDownList

Figura 12: Associar o valor do CategoryID produto à propriedade DropDownList s SelectedValue

Um último problema permanece: se o produto não tiver um CategoryID valor especificado, a instrução de vinculação de dados em SelectedValue resultará em uma exceção. Isso ocorre porque o DropDownList contém apenas itens para as categorias e não oferece uma opção para os produtos que têm um NULL valor de banco de dados para CategoryID. Para corrigir isso, defina a propriedade true dropDownList como AppendDataBoundItems e adicione um novo item ao DropDownList, omitindo a Value propriedade da sintaxe declarativa. Ou seja, verifique se a Categories sintaxe declarativa do DropDownList é semelhante à seguinte:

<asp:DropDownList ID="Categories" runat="server" AppendDataBoundItems="True" 
    DataSourceID="CategoriesDataSource" DataTextField="CategoryName" 
    DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
    <asp:ListItem Value=">-- Select One --</asp:ListItem>
</asp:DropDownList>

Observe como o <asp:ListItem Value=""> -- Select One -- tem seu Value atributo definido explicitamente como uma cadeia de caracteres vazia. Consulte o tutorial Personalizando a Interface de Modificação de Dados para obter uma discussão mais detalhada sobre por que esse item DropDownList adicional é necessário para lidar com o NULL caso e por que a Value atribuição da propriedade a uma cadeia de caracteres vazia é essencial.

Observação

Há um possível problema de desempenho e escalabilidade aqui que vale a pena mencionar. Como cada linha tem um DropDownList que usa o CategoriesDataSource como sua fonte de dados, o CategoriesBLL método de classe s GetCategories será chamado n vezes por visita de página, em que n é o número de linhas no GridView. Essas n chamadas para GetCategories resultar em n consultas para o banco de dados. Esse impacto no banco de dados pode ser diminuído armazenando em cache as categorias retornadas em um cache por solicitação ou por meio da Camada de Cache usando uma dependência de cache SQL ou uma expiração baseada em tempo muito curta.

Etapa 4: Concluindo a interface de edição

Fizemos várias alterações nos modelos do GridView sem pausar para ver nosso progresso. Reserve um momento para ver nosso progresso por meio de um navegador. Como mostra a Figura 13, cada linha é renderizada usando seu ItemTemplate, que contém a interface de edição da célula.

Cada linha GridView é editável

Figura 13: cada linha gridview é editável (clique para exibir imagem em tamanho real)

Há alguns pequenos problemas de formatação que devemos cuidar neste momento. Primeiro, observe que o UnitPrice valor contém quatro pontos decimais. Para corrigir isso, retorne ao UnitPrice TemplateField s ItemTemplate e, na marca inteligente TextBox, clique no link Editar DataBindings. Em seguida, especifique que a Text propriedade deve ser formatada como um número.

Formatar a propriedade Text como um número

Figura 14: formatar a Text propriedade como um número

Em segundo lugar, vamos centralizar a caixa de seleção na Discontinued coluna (em vez de tê-la alinhada à esquerda). Clique em Editar Colunas na marca inteligente GridView e selecione TemplateField Discontinued na lista de campos no canto inferior esquerdo. Faça uma busca detalhada ItemStyle e defina a HorizontalAlign propriedade como Central, conforme mostrado na Figura 15.

Centralizar a Caixa de Seleção Descontinuada

Figura 15: Centralizar a Caixa de Seleção Discontinued

Em seguida, adicione um controle ValidationSummary à página e defina sua ShowMessageBox propriedade como true e sua ShowSummary propriedade como false. Adicione também os controles Da Web de Botão que, quando clicados, atualizarão as alterações do usuário. Especificamente, adicione dois controles Web button, um acima do GridView e outro abaixo dele, definindo ambas as propriedades de controles Text como Atualizar Produtos.

Como a interface de edição do GridView é definida em seus TemplateFields ItemTemplate s, os EditItemTemplate s são supérfluos e podem ser excluídos.

Depois de fazer as alterações de formatação mencionadas acima, adicionar os controles Button e remover os s desnecessários EditItemTemplate , a sintaxe declarativa da página deve ser semelhante à seguinte:

<p>
    <asp:Button ID="UpdateAllProducts1" runat="server" Text="Update Products" />
</p>
<p>
    <asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
        AllowPaging="True" AllowSorting="True">
        <Columns>
            <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
                <ItemTemplate>
                    <asp:TextBox ID="ProductName" runat="server" 
                        Text='<%# Bind("ProductName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                        ControlToValidate="ProductName"
                        ErrorMessage="You must provide the product's name." 
                        runat="server">*</asp:RequiredFieldValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Category" 
                SortExpression="CategoryName">
                <ItemTemplate>
                    <asp:DropDownList ID="Categories" runat="server" 
                        AppendDataBoundItems="True" 
                        DataSourceID="CategoriesDataSource"
                        DataTextField="CategoryName" 
                        DataValueField="CategoryID" 
                        SelectedValue='<%# Bind("CategoryID") %>'>
                        <asp:ListItem>-- Select One --</asp:ListItem>
                    </asp:DropDownList>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Price" 
                SortExpression="UnitPrice">
                <ItemTemplate>
                    $<asp:TextBox ID="UnitPrice" runat="server" Columns="8" 
                        Text='<%# Bind("UnitPrice", "{0:N}") %>'></asp:TextBox>
                    <asp:CompareValidator ID="CompareValidator1" runat="server" 
                        ControlToValidate="UnitPrice"
                        ErrorMessage="You must enter a valid currency value. 
                                      Please omit any currency symbols."
                        Operator="GreaterThanEqual" Type="Currency" 
                        ValueToCompare="0">*</asp:CompareValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
                <ItemTemplate>
                    <asp:CheckBox ID="Discontinued" runat="server" 
                        Checked='<%# Bind("Discontinued") %>' />
                </ItemTemplate>
                <ItemStyle HorizontalAlign="Center" />
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
</p>
<p>
    <asp:Button ID="UpdateAllProducts2" runat="server" Text="Update Products" />
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
    <asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetCategories" TypeName="CategoriesBLL">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="ValidationSummary1" runat="server" 
        ShowMessageBox="True" ShowSummary="False" />
</p>

A Figura 16 mostra essa página quando exibida por meio de um navegador após a adição dos controles da Web de botão e as alterações de formatação feitas.

A página agora inclui dois botões de produtos de atualização

Figura 16: a página agora inclui dois botões de produtos de atualização (clique para exibir a imagem em tamanho real)

Etapa 5: Atualizando os produtos

Quando um usuário visita esta página, ele fará suas modificações e, em seguida, clicará em um dos dois botões Atualizar Produtos. Nesse ponto, precisamos de alguma forma salvar os valores inseridos pelo usuário para cada linha em uma ProductsDataTable instância e, em seguida, passá-los para um método BLL que, em seguida, passará essa ProductsDataTable instância para o método da DAL s UpdateWithTransaction . O UpdateWithTransaction método, que criamos no tutorial anterior, garante que o lote de alterações seja atualizado como uma operação atômica.

Crie um método chamado BatchUpdate em BatchUpdate.aspx.cs e adicione o seguinte código:

private void BatchUpdate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Find the ProductsRow instance in products that maps to gvRow
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        Northwind.ProductsRow product = products.FindByProductID(productID);
        if (product != null)
        {
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateWithTransaction(products);
}

Esse método começa obtendo todos os produtos de volta em um ProductsDataTable por meio de uma chamada para o método S da GetProducts BLL. Em seguida, ele enumera a ProductGrid coleção GridView.Rows A Rows coleção contém uma GridViewRow instância para cada linha exibida no GridView. Como estamos mostrando no máximo dez linhas por página, a coleção GridView Rows não terá mais de dez itens.

Para cada linha, o ProductID é capturado da DataKeys coleção e o apropriado ProductsRow é selecionado no ProductsDataTable. Os quatro controles de entrada TemplateField são referenciados programaticamente e seus valores atribuídos às ProductsRow propriedades da instância. Depois que cada valor de linha GridView tiver sido usado para atualizar o ProductsDataTable, ele será passado para o método S UpdateWithTransaction da BLL que, como vimos no tutorial anterior, simplesmente chama para baixo o método da DAL s UpdateWithTransaction .

O algoritmo de atualização em lote usado para este tutorial atualiza cada linha no ProductsDataTable que corresponde a uma linha no GridView, independentemente de as informações do produto 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. De volta ao tutorial Executando o Lote Atualizações exploramos uma interface de atualização em lote com a DataList e adicionamos código que atualizaria apenas os registros que foram realmente modificados pelo usuário. Fique à vontade para usar as técnicas de Executar Atualizações do Lote para atualizar o código neste tutorial, se desejado.

Observação

Ao associar a fonte de dados ao GridView por meio de sua marca inteligente, o Visual Studio atribui automaticamente os valores de chave primária da fonte de dados à propriedade GridView DataKeyNames . Se você não associou ObjectDataSource ao GridView por meio da marca inteligente GridView, conforme descrito na Etapa 1, será necessário definir manualmente a propriedade GridView DataKeyNames como ProductID para acessar o ProductID valor de cada linha por meio da DataKeys coleção.

O código usado em BatchUpdate é semelhante ao usado nos métodos de UpdateProduct BLL, a main diferença é que, nos UpdateProduct métodos, apenas uma única ProductRow instância é recuperada da arquitetura. O código que atribui as propriedades do ProductRow é o mesmo entre os UpdateProducts métodos e o código dentro do foreach loop em BatchUpdate, como é o padrão geral.

Para concluir este tutorial, precisamos ter o BatchUpdate método invocado quando um dos botões Atualizar Produtos for clicado. Crie manipuladores de eventos para os Click eventos desses dois controles Button e adicione o seguinte código nos manipuladores de eventos:

BatchUpdate();
ClientScript.RegisterStartupScript(this.GetType(), "message", 
    "alert('The products have been updated.');", true);

Primeiro, uma chamada é feita para BatchUpdate. Em seguida, o ClientScript property é usado para injetar JavaScript que exibirá uma caixa de mensagem que lê Os produtos foram atualizados.

Tire um minuto para testar esse código. Visite BatchUpdate.aspx um navegador, edite várias linhas e clique em um dos botões Atualizar Produtos. Supondo que não haja erros de validação de entrada, você deverá ver uma caixa de mensagem que lê Os produtos foram atualizados. Para verificar a atomicidade da atualização, considere adicionar uma restrição aleatória CHECK , como uma que não permite UnitPrice valores de 1234,56. Em seguida, em BatchUpdate.aspx, edite vários registros, certificando-se de definir um dos valores do UnitPrice produto para o valor proibido ( 1234,56 ). Isso deve resultar em um erro ao clicar em Atualizar Produtos com as outras alterações durante essa operação em lote revertida para seus valores originais.

Um método alternativoBatchUpdate

O BatchUpdate método que acabamos de examinar recupera todos os produtos do método S da GetProducts BLL e atualiza apenas os registros que aparecem no GridView. Essa abordagem é ideal se o GridView não usar paginação, mas, se isso acontecer, poderá haver centenas, milhares ou dezenas de milhares de produtos, mas apenas dez linhas no GridView. Nesse caso, obter todos os produtos do banco de dados apenas para modificar 10 deles é menor que o ideal.

Para esses tipos de situações, considere usar o seguinte BatchUpdateAlternate método em vez disso:

private void BatchUpdateAlternate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Create a new ProductRow instance
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        
        Northwind.ProductsDataTable currentProductDataTable = 
            productsAPI.GetProductByProductID(productID);
        if (currentProductDataTable.Rows.Count > 0)
        {
            Northwind.ProductsRow product = currentProductDataTable[0];
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
            // Import the ProductRow into the products DataTable
            products.ImportRow(product);
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateProductsWithTransaction(products);
}

BatchMethodAlternate começa criando um novo vazio ProductsDataTable chamado products. Em seguida, ele percorre a coleção GridView Rows e, para cada linha, obtém as informações específicas do produto usando o método S da GetProductByProductID(productID) BLL. A instância recuperada ProductsRow tem suas propriedades atualizadas da mesma forma BatchUpdateque , mas depois de atualizar a linha, ela é importada para o products``ProductsDataTable por meio do método DataTable sImportRow(DataRow).

Após a conclusão do foreach loop, products contém uma ProductsRow instância para cada linha no GridView. Como cada uma das ProductsRow instâncias foi adicionada ao products (em vez de atualizada), se passarmos cegamente para o UpdateWithTransaction método , o ProductsTableAdapter tentará inserir cada um dos registros no banco de dados. Em vez disso, precisamos especificar que cada uma dessas linhas foi modificada (não adicionada).

Isso pode ser feito adicionando um novo método à BLL chamada UpdateProductsWithTransaction. UpdateProductsWithTransaction, mostrado abaixo, define o RowState de cada uma das ProductsRow instâncias no para Modified e, em ProductsDataTable seguida, passa o ProductsDataTable para o método DAL sUpdateWithTransaction.

public int UpdateProductsWithTransaction(Northwind.ProductsDataTable products)
{
    // Mark each product as Modified
    products.AcceptChanges();
    foreach (Northwind.ProductsRow product in products)
        product.SetModified();
    // Update the data via a transaction
    return UpdateWithTransaction(products);
}

Resumo

O GridView fornece recursos internos de edição por linha, mas não tem suporte para criar interfaces totalmente editáveis. Como vimos neste tutorial, essas interfaces são possíveis, mas exigem um pouco de trabalho. Para criar um GridView em que cada linha é editável, precisamos converter os campos do GridView em TemplateFields e definir a interface de edição dentro dos ItemTemplate s. Além disso, atualizar todos os tipos controles da Web de botão devem ser adicionados à página, separados do GridView. Esses manipuladores de eventos Buttons Click precisam enumerar a coleção GridView Rows , armazenar as alterações em um ProductsDataTablee passar as informações atualizadas para o método BLL apropriado.

No próximo tutorial, veremos como criar uma interface para exclusão em lote. Em particular, cada linha GridView incluirá uma caixa de seleção e, em vez de atualizar todos os botões de tipo, teremos botões Excluir Linhas Selecionadas.

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 Teresa Murphy e David Suru. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, solte-me uma linha em mitchell@4GuysFromRolla.com.