Executar atualizações em lote (VB)
por Scott Mitchell
Saiba como criar uma 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. Como o GridView editável padrão, cada item na DataList incluía um botão Editar que, quando clicado, tornaria o item editável. Embora essa edição no nível do 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, a quantidade de cliques poderá prejudicar sua produtividade. Nessas situações, uma opção melhor é fornecer um DataList totalmente editável, um em que todos os seus itens estejam no modo de edição e cujos valores possam ser editados clicando em um botão Atualizar tudo na página (consulte a Figura 1).
Figura 1: Cada item em uma lista de dados totalmente editável pode ser modificado (clique para exibir a imagem em tamanho real)
Neste tutorial, examinaremos como permitir que os usuários atualizem as informações de endereço dos fornecedores usando uma DataList totalmente editável.
Etapa 1: Criar a interface do usuário editável no DataList ItemTemplate
No tutorial anterior, em que criamos uma 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 Web TextBox).
A DataList propriedade determina EditItemIndex
o que DataListItem
(se houver) é renderizado usando o EditItemTemplate
. Em particular, o DataListItem
cujo ItemIndex
valor corresponde à DataList propriedade é EditItemIndex
renderizado usando o EditItemTemplate
. Esse modelo funciona bem quando apenas um item pode ser editado por vez, mas desmorona ao criar um DataList totalmente editável.
Para um DataList totalmente editável, queremos que todos os DataListItem
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 do fornecedor, 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 ID
propriedade como Suppliers
. Na marca inteligente da DataList, opte por adicionar um novo controle 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 método da GetSuppliers()
classe (consulte a SuppliersBLL
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 Lógica de Negócios. Portanto, defina a lista suspensa como (Nenhum) na guia UPDATE (consulte a Figura 4).
Figura 3: Recuperar informações do fornecedor usando o método (clique para exibir a GetSuppliers()
imagem em tamanho real)
Figura 4: Defina a lista suspensa como (Nenhuma) na guia ATUALIZAR (clique para exibir a imagem em tamanho real)
Depois de concluir o assistente, o Visual Studio gera automaticamente o DataList para ItemTemplate
exibir cada campo de dados retornado pela fonte de dados em um controle Web de rótulo. Precisamos modificar este 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 da DataList ou diretamente por meio da sintaxe declarativa.
Reserve um momento para criar uma interface de edição que exiba o nome do fornecedor como texto, mas inclua 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 I, 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 e ProductPropertyValue
CSS.
.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.
Figura 5: Cada fornecedor na lista de dados é editável (clique para exibir a imagem em tamanho real)
Etapa 2: Adicionando 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 no 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 qualquer um dos botões tenha o mesmo efeito).
Comece adicionando um controle Web Button acima do DataList e defina sua ID
propriedade como UpdateAll1
. Em seguida, adicione o segundo controle Web Button abaixo do DataList, definindo seu ID
nome como UpdateAll2
. Defina as Text
propriedades dos dois Botões para 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
, fazendo com que os manipuladores de eventos simplesmente invoquem esse terceiro método.
Protected Sub UpdateAll1_Click(sender As Object, e As EventArgs) _
Handles UpdateAll1.Click
UpdateAllSupplierAddresses()
End Sub
Protected Sub UpdateAll2_Click(sender As Object, e As EventArgs) _
Handles UpdateAll2.Click
UpdateAllSupplierAddresses()
End Sub
Private Sub UpdateAllSupplierAddresses()
' TODO: Write code to update _all_ of the supplier addresses in the DataList
End Sub
A Figura 6 mostra a página depois que os botões Update All foram adicionados.
Figura 6: Dois botões Atualizar tudo foram adicionados à página (clique para exibir a imagem em tamanho real)
Etapa 3: Atualizando todas as informações de endereço dos fornecedores
Com todos os itens do 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 percorrer os itens do DataList e chamar o SuppliersBLL
método da UpdateSupplierAddress
classe para cada um.
A coleção de instâncias que compõem o DataList pode ser acessada DataListItem
por meio da DataList propriedade .Items
Com uma referência a um DataListItem
, podemos pegar o correspondente SupplierID
da DataKeys
coleção e referenciar programaticamente os controles Web TextBox dentro do ItemTemplate
como ilustra o código a seguir:
Private Sub UpdateAllSupplierAddresses()
' Create an instance of the SuppliersBLL class
Dim suppliersAPI As New SuppliersBLL()
' Iterate through the DataList's items
For Each item As DataListItem In Suppliers.Items
' Get the supplierID from the DataKeys collection
Dim supplierID As Integer = Convert.ToInt32(Suppliers.DataKeys(item.ItemIndex))
' Read in the user-entered values
Dim address As TextBox = CType(item.FindControl("Address"), TextBox)
Dim city As TextBox = CType(item.FindControl("City"), TextBox)
Dim country As TextBox = CType(item.FindControl("Country"), TextBox)
Dim addressValue As String = Nothing, _
cityValue As String = Nothing, _
countryValue As String = Nothing
If address.Text.Trim().Length > 0 Then
addressValue = address.Text.Trim()
End If
If city.Text.Trim().Length > 0 Then
cityValue = city.Text.Trim()
End If
If country.Text.Trim().Length > 0 Then
countryValue = country.Text.Trim()
End If
' Call the SuppliersBLL class's UpdateSupplierAddress method
suppliersAPI.UpdateSupplierAddress _
(supplierID, addressValue, cityValue, countryValue)
Next
End Sub
Quando o usuário clica em um dos botões Atualizar tudo, o UpdateAllSupplierAddresses
método itera por meio de cada um DataListItem
no Suppliers
DataList e chama o SuppliersBLL
método da UpdateSupplierAddress
classe, passando os valores correspondentes. Um valor não inserido para endereço, cidade ou país/região é um valor de to UpdateSupplierAddress
(em vez de uma cadeia de caracteres em branco), o que resulta em um banco de Nothing
dados NULL
para os campos do registro subjacente.
Observação
Como aprimoramento, talvez você queira adicionar um controle Web de rótulo de status à página que fornece alguma mensagem de confirmação após a execução da atualização em lote.
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 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. Por exemplo, se você usar gatilhos para registrar todos os UPDATE
s da Suppliers
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 são 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 uma DataTable é preenchida 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 especificado lendo primeiro o registro de fornecedor único em a SuppliersDataTable
e, em seguida, definimos os valores de Address
coluna , City
e Country
usando o seguinte código:
Public Function UpdateSupplierAddress _
(supplierID As Integer, address As String, city As String, country As String) _
As Boolean
Dim suppliers As Northwind.SuppliersDataTable = _
Adapter.GetSupplierBySupplierID(supplierID)
If suppliers.Count = 0 Then
' no matching record found, return false
Return False
Else
Dim supplier As Northwind.SuppliersRow = suppliers(0)
If address Is Nothing Then
supplier.SetAddressNull()
Else
supplier.Address = address
End If
If city Is Nothing Then
supplier.SetCityNull()
Else
supplier.City = city
End If
If country Is Nothing Then
supplier.SetCountryNull()
Else
supplier.Country = country
End If
' Update the supplier Address-related information
Dim rowsAffected As Integer = Adapter.Update(supplier)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End If
End Function
Esse código atribui ingenuamente os valores de endereço, cidade e país/região passados ao SuppliersRow
independentemente SuppliersDataTable
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 foi SupplierRow
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 diferirem dos SuppliersRow
valores existentes s. No caso em que o endereço, a cidade e o país/região são os mesmos que os dados existentes, nenhuma alteração será feita e o SupplierRow
s RowState
será deixado marcado como inalterado. O resultado líquido é que, quando o método do Update
DAL é chamado, nenhuma chamada de banco de dados será feita porque o SuppliersRow
não foi modificado.
Para decretar 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 Is Nothing AndAlso Not supplier.IsAddressNull() Then
supplier.SetAddressNull()
ElseIf (address IsNot Nothing AndAlso supplier.IsAddressNull) _
OrElse (Not supplier.IsAddressNull() AndAlso _
String.Compare(supplier.Address, address) <> 0) Then
supplier.Address = address
End If
If city Is Nothing AndAlso Not supplier.IsCityNull() Then
supplier.SetCityNull()
ElseIf (city IsNot Nothing AndAlso supplier.IsCityNull) _
OrElse (Not supplier.IsCityNull() AndAlso _
String.Compare(supplier.City, city) <> 0) Then
supplier.City = city
End If
If country Is Nothing AndAlso Not supplier.IsCountryNull() Then
supplier.SetCountryNull()
ElseIf (country IsNot Nothing AndAlso supplier.IsCountryNull) _
OrElse (Not supplier.IsCountryNull() AndAlso _
String.Compare(supplier.Country, country) <> 0) Then
supplier.Country = country
End If
Com esse código adicionado, o método da Update
DAL envia uma UPDATE
instrução ao banco de dados apenas para os registros cujos valores relacionados ao endereço foram alterados.
Como alternativa, podemos acompanhar se há diferenças entre os campos de endereço passados e os dados do banco de dados e, se não houver nenhuma, simplesmente ignorar a chamada para o método da Update
DAL. Essa abordagem funciona bem se você estiver usando o método direto do banco de dados, já que o método direto do banco de dados não é passado uma SuppliersRow
instância que RowState
pode ser verificada para determinar se uma chamada de banco de dados é realmente necessária.
Observação
Cada vez 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 alguma alteração nos dados, outra chamada ao 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 somente os registros em que ocorreram alterações poderiam ser atualizados.
Resumo
Neste tutorial, vimos como criar uma DataList totalmente editável, permitindo que um usuário modifique rapidamente as informações de endereço de vários fornecedores. Começamos definindo a interface de edição como um controle Web TextBox para os valores de endereço, cidade e país/região do fornecedor no DataList ItemTemplate
. Em seguida, adicionamos os botões Atualizar tudo acima e abaixo da DataList. Depois que um usuário faz suas alterações e clica em um dos botões Atualizar tudo, o DataListItem
s é enumerado e uma chamada para o SuppliersBLL
método da UpdateSupplierAddress
classe é feita.
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 Zack Jones e Ken Pespisa. Interessado em revisar meus próximos artigos do MSDN? Em caso afirmativo, envie-me uma mensagem para mitchell@4GuysFromRolla.com.