Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
por Scott Mitchell
Saiba como excluir vários registros de banco de dados em uma única operação. Na Camada de Interface do Usuário, ampliamos um GridView aprimorado que foi criado em um tutorial anterior. Na Camada de Acesso a Dados, encapsulamos as várias operações de Exclusão em uma transação para garantir que todas as exclusões sejam bem-sucedidas ou todas as exclusões sejam revertidas.
Introdução
O tutorial anterior explorou como criar uma interface de edição em lote usando um GridView totalmente editável. Em situações em que os usuários geralmente editam muitos registros de uma só vez, uma interface de edição em lote exigirá muito menos postbacks e comutadores de contexto de teclado para mouse, melhorando assim a eficiência do usuário final. Essa técnica é igualmente útil para páginas em que é comum que os usuários excluam muitos registros de uma só vez.
Qualquer pessoa que tenha usado um cliente de email online já está familiarizada com uma das interfaces de exclusão em lote mais comuns: uma caixa de seleção em cada linha em uma grade com um botão Excluir Todos os Itens Verificados correspondente (consulte a Figura 1). Este tutorial é bastante curto porque já fizemos todo o trabalho árduo em tutoriais anteriores na criação da interface baseada na Web e um método para excluir uma série de registros como uma única operação atômica. No tutorial Adicionando uma Coluna GridView de Checkboxes, criamos um GridView com uma coluna de checkboxes e, no tutorial Encapsulando Modificações de Banco de Dados dentro de uma Transação, criamos um método na BLL que usaria uma transação para excluir um List<T> dos ProductID valores. Neste tutorial, vamos criar e mesclar nossas experiências anteriores para desenvolver um exemplo funcional de exclusão em lote.
Figura 1: cada linha inclui uma caixa de seleção (clique para exibir a imagem em tamanho real)
Etapa 1: Criando a interface de exclusão em lote
Como já criamos a interface de exclusão em lote para caixas de seleção no tutorial Adicionando uma coluna GridView de caixas de seleção, podemos simplesmente copiá-la em BatchDelete.aspx vez de criá-la do zero. Comece abrindo a BatchDelete.aspx página na BatchData pasta e na CheckBoxField.aspx página na EnhancedGridView pasta. Na página CheckBoxField.aspx, vá para a visão de origem e copie o código entre as <asp:Content> tags, como mostrado na Figura 2.
Figura 2: Copiar a Marcação Declarativa para a Área de CheckBoxField.aspx Transferência (Clique para exibir a imagem em tamanho real)
Em seguida, vá para a visualização do código em BatchDelete.aspx e cole o conteúdo da área de transferência dentro das <asp:Content> tags. Também copie e cole o código de dentro da classe code-behind em CheckBoxField.aspx.vb para a classe code-behind em BatchDelete.aspx.vb (o manipulador de eventos do botão DeleteSelectedProductsClick, o método ToggleCheckState, e os manipuladores de eventos Click para os botões CheckAll e UncheckAll). Depois de copiar esse conteúdo, a BatchDelete.aspx classe code-behind da página deve conter o seguinte código:
Partial Class BatchData_BatchDelete
Inherits System.Web.UI.Page
Protected Sub DeleteSelectedProducts_Click(sender As Object, e As EventArgs) _
Handles DeleteSelectedProducts.Click
Dim atLeastOneRowDeleted As Boolean = False
' Iterate through the Products.Rows property
For Each row As GridViewRow In Products.Rows
' Access the CheckBox
Dim cb As CheckBox = row.FindControl("ProductSelector")
If cb IsNot Nothing AndAlso cb.Checked Then
' Delete row! (Well, not really...)
atLeastOneRowDeleted = True
' First, get the ProductID for the selected row
Dim productID As Integer = _
Convert.ToInt32(Products.DataKeys(row.RowIndex).Value)
' "Delete" the row
DeleteResults.Text &= String.Format _
("This would have deleted ProductID {0}<br />", productID)
'... To actually delete the product, use ...
' Dim productAPI As New ProductsBLL
' productAPI.DeleteProduct(productID)
'............................................
End If
Next
' Show the Label if at least one row was deleted...
DeleteResults.Visible = atLeastOneRowDeleted
End Sub
Private Sub ToggleCheckState(ByVal checkState As Boolean)
' Iterate through the Products.Rows property
For Each row As GridViewRow In Products.Rows
' Access the CheckBox
Dim cb As CheckBox = row.FindControl("ProductSelector")
If cb IsNot Nothing Then
cb.Checked = checkState
End If
Next
End Sub
Protected Sub CheckAll_Click(sender As Object, e As EventArgs) _
Handles CheckAll.Click
ToggleCheckState(True)
End Sub
Protected Sub UncheckAll_Click(sender As Object, e As EventArgs) _
Handles UncheckAll.Click
ToggleCheckState(False)
End Sub
End Class
Depois de copiar a marcação declarativa e o código-fonte, dedique um momento para testá-lo BatchDelete.aspx ao visualizá-lo em um navegador. Você deve ver um GridView listando os dez primeiros produtos, com cada linha mostrando o nome do produto, a categoria e o preço, juntamente com uma caixa de seleção. Deve haver três botões: Verificar Tudo, Desmarcar Tudo e Excluir Produtos Selecionados. Clicar no botão Verificar Tudo seleciona todas as caixas de seleção, enquanto Desmarcar Tudo limpa todas as caixas de seleção. Clicar em Excluir Produtos Selecionados exibe uma mensagem que lista os ProductID valores dos produtos selecionados, mas não exclui os produtos.
Figura 3: A interface de CheckBoxField.aspx foi movida para BatchDeleting.aspx (clique para exibir a imagem em tamanho real)
Etapa 2: Excluindo os produtos verificados usando transações
Com a interface de exclusão em lote copiada com êxito para BatchDeleting.aspx, tudo o que resta é atualizar o código para que o botão Excluir Produtos Selecionados exclua os produtos selecionados usando o método DeleteProductsWithTransaction na classe ProductsBLL. Esse método, adicionado nas Modificações de Banco de Dados Envolvidas em uma Transação, aceita como entrada um List(Of T) de ProductID valores e exclui cada ProductID correspondente dentro do escopo de uma transação.
O DeleteSelectedProducts manipulador de eventos do Click Botão atualmente usa o seguinte For Each loop para iterar por cada linha do GridView:
' Iterate through the Products.Rows property
For Each row As GridViewRow In Products.Rows
' Access the CheckBox
Dim cb As CheckBox = row.FindControl("ProductSelector")
If cb IsNot Nothing AndAlso cb.Checked Then
' Delete row! (Well, not really...)
atLeastOneRowDeleted = True
' First, get the ProductID for the selected row
Dim productID As Integer = _
Convert.ToInt32(Products.DataKeys(row.RowIndex).Value)
' "Delete" the row
DeleteResults.Text &= String.Format _
("This would have deleted ProductID {0}<br />", productID)
'... To actually delete the product, use ...
' Dim productAPI As New ProductsBLL
' productAPI.DeleteProduct(productID)
'............................................
End If
Next
Para cada linha, o ProductSelector controle Web CheckBox é referenciado programaticamente. Se for verificado, a linha ProductID será recuperada da coleção DataKeys e a propriedade DeleteResults Label s Text será atualizada para incluir uma mensagem indicando que a linha foi selecionada para ser excluída.
O código acima não exclui nenhum registro, pois a chamada para o ProductsBLL método da Delete classe é comentada. Se essa lógica de exclusão fosse aplicada, o código excluiria os produtos, mas não dentro de uma operação atômica. Ou seja, se as primeiras exclusões na sequência tiverem sido bem-sucedidas, mas uma falha ocorrer mais tarde (talvez devido a uma violação de restrição de chave estrangeira), uma exceção será lançada. No entanto, os produtos já excluídos permanecerão excluídos.
Para garantir a atomicidade, precisamos usar, em vez disso, o método da classe ProductsBLLDeleteProductsWithTransaction. Como esse método aceita uma lista de ProductID valores, precisamos primeiro compilar essa lista da grade e passá-la como um parâmetro. Primeiramente, criamos uma instância de List(Of T) do tipo Integer. Dentro do For Each loop, precisamos adicionar os valores dos produtos ProductID selecionados a este List(Of T). Depois do loop, isso List(Of T) deve ser passado para o método ProductsBLL da classe DeleteProductsWithTransaction. Atualize o manipulador de eventos do Botão DeleteSelectedProductsClick com o seguinte código:
Protected Sub DeleteSelectedProducts_Click(sender As Object, e As EventArgs) _
Handles DeleteSelectedProducts.Click
' Create a List to hold the ProductID values to delete
Dim productIDsToDelete As New System.Collections.Generic.List(Of Integer)
' Iterate through the Products.Rows property
For Each row As GridViewRow In Products.Rows
' Access the CheckBox
Dim cb As CheckBox = CType(row.FindControl("ProductSelector"), CheckBox)
If cb IsNot Nothing AndAlso cb.Checked Then
' Save the ProductID value for deletion
' First, get the ProductID for the selected row
Dim productID As Integer = _
Convert.ToInt32(Products.DataKeys(row.RowIndex).Value)
' Add it to the List...
productIDsToDelete.Add(productID)
' Add a confirmation message
DeleteResults.Text &= String.Format _
("ProductID {0} has been deleted<br />", productID)
End If
Next
' Call the DeleteProductsWithTransaction method and show the Label
' if at least one row was deleted...
If productIDsToDelete.Count > 0 Then
Dim productAPI As New ProductsBLL()
productAPI.DeleteProductsWithTransaction(productIDsToDelete)
DeleteResults.Visible = True
' Rebind the data to the GridView
Products.DataBind()
End If
End Sub
O código atualizado cria um List(Of T) tipo Integer (productIDsToDelete) e o preenche com os ProductID valores a serem excluídos. Após o For Each loop, se houver pelo menos um produto selecionado, o método da ProductsBLL classe DeleteProductsWithTransaction será chamado e passado essa lista. O DeleteResults Rótulo também é exibido e os dados são recuperados para o GridView (para que os registros recém-excluídos não apareçam mais como linhas na grade).
A Figura 4 mostra o GridView depois que várias linhas foram selecionadas para exclusão. A Figura 5 mostra a tela imediatamente após o botão Excluir Produtos Selecionados ser clicado. Observe que, na Figura 5, os ProductID valores dos registros excluídos são exibidos no Rótulo abaixo do GridView e essas linhas não estão mais no GridView.
Figura 4: Os produtos selecionados serão excluídos (clique para exibir a imagem em tamanho real)
Figura 5: Os valores de produtos ProductID excluídos são listados abaixo do GridView (clique para exibir a imagem em tamanho real)
Observação
Para testar a DeleteProductsWithTransaction atomicidade do método, adicione manualmente uma entrada para um produto na Order Details tabela e tente excluir esse produto (juntamente com outros). Você receberá uma violação de restrição de chave estrangeira ao tentar excluir o produto com um pedido associado, mas observe como as outras exclusões de produtos selecionadas são revertidas.
Resumo
A criação de uma interface de exclusão em lote envolve a adição de um GridView com uma coluna de caixas de seleção e um controle web de botão que, quando clicado, excluirá todas as linhas selecionadas como uma única operação atômica. Neste tutorial, criamos essa interface juntando trabalhos feitos em dois tutoriais anteriores, adicionando uma coluna gridview de caixas de seleção e encapsulando modificações de banco de dados em uma transação. No primeiro tutorial, criamos um GridView com uma coluna de caixas de seleção e, no último, implementamos um método na BLL que, quando passado um List(Of T) dos ProductID valores, excluía todos eles dentro do escopo de uma transação.
No próximo tutorial, criaremos uma interface para executar inserções em lote.
Divirta-se programando!
Sobre o autor
Scott Mitchell, autor de sete livros 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 alcançado em mitchell@4GuysFromRolla.com.
Agradecimentos Especiais a
Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Hilton Giesenow e Teresa Murphy. Interessado em revisar meus próximos artigos do MSDN? Se assim for, deixe-me uma linha em mitchell@4GuysFromRolla.com.