Compartilhar via


Atualização em lote (VB)

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 em uma transação para garantir que todas as atualizações sejam bem-sucedidas ou que 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 seja tratada como uma operação atômica, o que garante que todas as modificações falhem ou todas sejam bem-sucedidas. Com essa funcionalidade 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 de botões Editar, Atualizar e Cancelar. Em vez disso, há dois botões Atualizar Produtos na página que, quando clicados, enumeram as linhas do 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 Atualizações em Lote, criamos uma interface de edição em lote usando o controle DataList. Este tutorial difere do anterior, pois usa um GridView e a atualização em lote é executada dentro do escopo de uma transação. Depois de concluir este tutorial, recomendo que você retorne ao tutorial anterior e atualize-o para usar a funcionalidade relacionada à transação de banco de dados adicionada no tutorial anterior.

Examinando as etapas para tornar todas as linhas do GridView editáveis

Conforme discutido no tutorial Uma visão geral da inserção, atualização e exclusão de dados , o GridView oferece suporte interno para editar seus dados subjacentes por linha. Internamente, o GridView observa qual linha é editável por meio de sua EditIndex propriedade. À medida que 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, os campos dessa linha são renderizados usando suas interfaces de edição. Para BoundFields, a interface de edição é uma TextBox cuja Text propriedade recebe o valor do campo de dados especificado pela propriedade BoundField DataField . Para TemplateFields, o EditItemTemplate é usado no lugar do ItemTemplate.

Lembre-se de que o fluxo de trabalho de edição começa quando um usuário clica no botão Editar de uma linha. Isso causa um postback, define a propriedade GridView EditIndex como o índice da linha clicada e associa novamente os dados à grade. Quando o 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 EditIndex de -1 como 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 que cada linha seja 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 converteremos seus BoundFields e CheckBoxField em TemplateFields. Nas Etapas 2 e 3, moveremos as interfaces de edição do TemplateFields EditItemTemplate s para o s ItemTemplate .

Etapa 1: Exibindo informações do produto

Antes de nos preocuparmos em criar um GridView onde 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 ID como ProductsGrid e, em sua marca inteligente, opte por associá-lo a um novo ObjectDataSource chamado ProductsDataSource. Configure o ObjectDataSource para recuperar seus dados do ProductsBLL método da GetProducts classe.

Configurar o ObjectDataSource para usar a classe ProductsBLL

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

Recuperar os dados do produto usando o método GetProducts

Figura 3: Recuperar os dados do produto usando o método (clique para exibir a GetProducts 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 as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum)

Figura 4: Defina as listas suspensas 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 deve 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 o status descontinuado do produto. Remova todos os campos, exceto , ProductName, UnitPriceCategoryName, e Discontinued renomeie as HeaderText propriedades dos três primeiros campos para Produto, Categoria e Preço, respectivamente. Por fim, marque as caixas de seleção Habilitar Paginação e Habilitar Classificação na marca inteligente do 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 EditItemTemplate para seu ItemTemplate.

Observação

Exploramos a criação e personalização de TemplateFields no tutorial Personalizando a interface de modificação de dados. Percorreremos 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 uma atualização, não hesite em consultar este tutorial anterior.

Na marca inteligente do 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.

Converta os BoundFields e CheckBoxField existentes em TemplateFields

Figura 5: Converter os BoundFields e CheckBoxField existentes em TemplateFields

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

Etapa 2: Criando asProductName interfaces eUnitPriceDiscontinued editando

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

Vamos começar com o ProductName TemplateField. Clique no link Editar Modelos da marca inteligente do GridView e faça uma busca detalhada no ProductName TemplateField EditItemTemplate. Selecione o TextBox, copie-o para a área de transferência e cole-o no ProductName TemplateField ItemTemplate. Altere a propriedade do TextBox ID para ProductName.

Em seguida, adicione um RequiredFieldValidator ao ItemTemplate para garantir que o usuário forneça um valor para o nome de cada 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 um TextBox e um RequiredFieldValidator

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

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

Adicione também um CompareValidator ao 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 ControlToValidate validador como UnitPrice, sua ErrorMessage propriedade como Você deve inserir um valor de moeda válido. Por favor, omita quaisquer símbolos de moeda., sua Text propriedade para *, sua Type propriedade para Currency, sua Operator propriedade para GreaterThanEqual, e sua ValueToCompare propriedade para 0 .

Adicione um CompareValidator para garantir que o preço inserido seja um valor de moeda não negativo

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

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

Etapa 3: Criando aCategoryNameinterface de edição

A interface de edição no CategoryName TemplateField contém EditItemTemplate um TextBox que exibe o valor do CategoryName campo de 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 completas, elas são apresentadas de forma concisa. Para obter uma visão mais detalhada da criação e configuração das categorias DropDownList, consulte o tutorial Personalizando a interface de modificação de dados.

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

Configurar o ObjectDataSource para usar a classe CategoriesBLL

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

Recuperar os dados da categoria usando o método GetCategories

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

Como esse 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 as listas suspensas nas guias UPDATE e DELETE como (Nenhum)

Figura 10: Defina as listas suspensas 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 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 e ItemTemplate , na marca inteligente DropDownList , clique no link Escolher Fonte de Dados . No assistente de Configuração da Fonte de Dados, selecione a CategoriesDataSource opção na primeira lista suspensa e escolha ter CategoryName usado para a exibição e CategoryID como o valor.

Associar o DropDownList ao CategoriesDataSource

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

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 como o valor do CategoryID produto. Clique no link Edit DataBindings da marca inteligente DropDownList e associe a SelectedValue propriedade ao campo de dados, CategoryID conforme mostrado na Figura 12.

Associar o valor CategoryID do produto à propriedade DropDownList SelectedValue

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

Um último problema permanece: se o produto não tiver um CategoryID valor especificado, a instrução databinding on 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 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 -- <asp:ListItem Value=""> Select One -- tem seu Value atributo explicitamente definido como uma string vazia. Consulte o tutorial Personalizando a Interface de Modificação de Dados para obter uma discussão mais completa 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 da classe será GetCategories chamado n vezes por visita de página, em que n é o número de linhas no GridView. Essas n chamadas resultam GetCategories em n consultas ao 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 exibir 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 do GridView é editável

Figura 13: Cada linha do GridView é editável (clique para exibir a imagem em tamanho real)

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

Formatar a propriedade de texto 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 alinhá-la à esquerda). Clique em Editar Colunas da marca inteligente do GridView e selecione o Discontinued TemplateField na lista de campos no canto inferior esquerdo. Faça uma busca detalhada ItemStyle e defina a propriedade como Center, HorizontalAlign como mostra a Figura 15.

Centralizar a caixa de seleção Descontinuada

Figura 15: Centralizar a Discontinued caixa de seleção

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 Web de botão que, quando clicados, atualizarão as alterações do usuário. Especificamente, adicione dois controles Web de botão, um acima do GridView e outro abaixo dele, definindo ambas as propriedades de controle Text como Atualizar produtos.

Como a interface de edição do GridView é definida em seus TemplateFields ItemTemplate s, os s são supérfluos EditItemTemplate 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 depois que os controles Web de botão foram adicionados e as alterações de formatação feitas.

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

Figura 16: A página agora inclui dois botões Atualizar produtos (clique para exibir a imagem em tamanho real)

Etapa 5: Atualizando os produtos

Quando um usuário visita esta página, ele faz suas modificações e clica 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 passará essa ProductsDataTable instância para o método DAL 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 nomeado BatchUpdate em BatchUpdate.aspx.vb e adicione o seguinte código:

Private Sub BatchUpdate()
    ' Enumerate the GridView's Rows collection and create a ProductRow
    Dim productsAPI As New ProductsBLL()
    Dim products As Northwind.ProductsDataTable = productsAPI.GetProducts()
    For Each gvRow As GridViewRow In ProductsGrid.Rows
        ' Find the ProductsRow instance in products that maps to gvRow
        Dim productID As Integer = _
            Convert.ToInt32(ProductsGrid.DataKeys(gvRow.RowIndex).Value)
        Dim product As Northwind.ProductsRow = products.FindByProductID(productID)
        If product IsNot Nothing Then
            ' Programmatically access the form field elements in the 
            ' current GridViewRow
            Dim productName As TextBox = _
                CType(gvRow.FindControl("ProductName"), TextBox)
            Dim categories As DropDownList = _
                CType(gvRow.FindControl("Categories"), DropDownList)
            Dim unitPrice As TextBox = _
                CType(gvRow.FindControl("UnitPrice"), TextBox)
            Dim discontinued As CheckBox = _
                CType(gvRow.FindControl("Discontinued"), CheckBox)
            ' Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim()
            If categories.SelectedIndex = 0 Then 
                product.SetCategoryIDNull() 
            Else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue)
            End If
            If unitPrice.Text.Trim().Length = 0 Then 
                product.SetUnitPriceNull() 
            Else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text)
            End If
            product.Discontinued = discontinued.Checked
        End If
    Next
    ' Now have the BLL update the products data using a transaction
    productsAPI.UpdateWithTransaction(products)
End Sub

Esse método começa obtendo todos os produtos de volta em um ProductsDataTable por meio de uma chamada para o método da GetProducts BLL. Em seguida, ele enumera a ProductGrid coleção do Rows GridView. 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 do Rows GridView não terá mais de dez itens.

Para cada linha, o ProductID é obtido 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 propriedades da ProductsRow instância. Depois que os valores de cada linha GridView forem usados para atualizar o ProductsDataTable, ele será passado para o método da UpdateWithTransaction BLL que, como vimos no tutorial anterior, simplesmente chama o método da DAL 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 terem sido 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 do banco de dados. De volta ao tutorial Executando atualizações em lote, exploramos uma interface de atualização em lote com o DataList e adicionamos um código que atualizaria apenas os registros que foram realmente modificados pelo usuário. Sinta-se à vontade para usar as técnicas de Executando atualizações em lote para atualizar o código neste tutorial, se desejar.

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 do DataKeyNames GridView. Se você não associou o ObjectDataSource ao GridView por meio da marca inteligente do GridView, conforme descrito na Etapa 1, precisará definir manualmente a propriedade do GridView DataKeyNames como ProductID para acessar o valor de ProductID cada linha por meio da DataKeys coleção.

O código usado em BatchUpdate é semelhante ao usado nos métodos da UpdateProduct BLL, a principal 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 For Each loop em BatchUpdate, assim como o padrão geral.

Para concluir este tutorial, precisamos ter o BatchUpdate método invocado quando um dos botões Atualizar Produtos é 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(Me.GetType(), "message", _
    "alert('The products have been updated.');", True)

Primeiro, uma chamada é feita para BatchUpdate. Em seguida, a ClientScript propriedade é usada para injetar JavaScript que exibirá uma caixa de mensagem que diz Os produtos foram atualizados.

Reserve um minuto para testar este código. Visite BatchUpdate.aspx por meio de 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 diz 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.aspxedite 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 revertidas para seus valores originais.

Um método alternativoBatchUpdate

O BatchUpdate método que acabamos de examinar recupera todos os produtos do método da GetProducts BLL e, em seguida, atualiza apenas os registros que aparecem no GridView. Essa abordagem é ideal se o GridView não usar paginação, mas se usar, pode 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 é menos do que o ideal.

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

Private Sub BatchUpdateAlternate()
    ' Enumerate the GridView's Rows collection and create a ProductRow
    Dim productsAPI As New ProductsBLL()
    Dim products As New Northwind.ProductsDataTable()
    For Each gvRow As GridViewRow In ProductsGrid.Rows
        ' Create a new ProductRow instance
        Dim productID As Integer = _
            Convert.ToInt32(ProductsGrid.DataKeys(gvRow.RowIndex).Value)
        Dim currentProductDataTable As Northwind.ProductsDataTable = _
            productsAPI.GetProductByProductID(productID)
        If currentProductDataTable.Rows.Count > 0 Then
            Dim product As Northwind.ProductsRow = currentProductDataTable(0)
            Dim productName As TextBox = _
                CType(gvRow.FindControl("ProductName"), TextBox)
            Dim categories As DropDownList = _
                CType(gvRow.FindControl("Categories"), DropDownList)
            Dim unitPrice As TextBox = _
                CType(gvRow.FindControl("UnitPrice"), TextBox)
            Dim discontinued As CheckBox = _
                CType(gvRow.FindControl("Discontinued"), CheckBox)
            ' Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim()
            If categories.SelectedIndex = 0 Then 
                product.SetCategoryIDNull() 
            Else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue)
            End If
            If unitPrice.Text.Trim().Length = 0 Then 
                product.SetUnitPriceNull() 
            Else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text)
            End If
            product.Discontinued = discontinued.Checked
            ' Import the ProductRow into the products DataTable
            products.ImportRow(product)
        End If
    Next
    ' Now have the BLL update the products data using a transaction
    productsAPI.UpdateProductsWithTransaction(products)
End Sub

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

Após a conclusão do For Each loop, products contém uma ProductsRow instância para cada linha no GridView. Uma vez que cada uma das ProductsRow instâncias foi adicionada ao products (em vez de atualizado), se passarmos cegamente para o UpdateWithTransaction método, ele 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 chamado UpdateProductsWithTransaction. UpdateProductsWithTransaction, mostrado abaixo, define o RowState de cada uma das ProductsRow instâncias no ProductsDataTable to Modified e, em seguida, passa o ProductsDataTable para o método do UpdateWithTransaction DAL.

Public Function UpdateProductsWithTransaction _
    (ByVal products As Northwind.ProductsDataTable) As Integer
    ' Mark each product as Modified
    products.AcceptChanges()
    For Each product As Northwind.ProductsRow In products
        product.SetModified()
    Next
    ' Update the data via a transaction
    Return UpdateWithTransaction(products)
End Function

Resumo

O GridView fornece recursos internos de edição por linha, mas não tem suporte para a criação de 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 do ItemTemplate s. Além disso, os controles Web de botão do tipo Atualizar todos devem ser adicionados à página, separados do GridView. Esses manipuladores de eventos Buttons Click precisam enumerar a coleção do 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 do GridView incluirá uma caixa de seleção e, em vez de botões do tipo Atualizar Tudo, teremos botões Excluir Linhas Selecionadas.

Boa programação!

Sobre o autor

Scott Mitchell, autor de sete livros ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Web da Microsoft desde 1998. Scott trabalha como consultor, instrutor e escritor independente. 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 a

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? Em caso afirmativo, envie-me uma mensagem para mitchell@4GuysFromRolla.com.