Inserção em lote (VB)

por Scott Mitchell

Baixar PDF

Saiba como inserir vários registros de banco de dados em uma única operação. Na Camada de Interface do Usuário, estendemos o GridView para permitir que o usuário insira vários novos registros. Na Camada de Acesso a Dados, encapsulamos as várias operações inserir dentro de uma transação para garantir que todas as inserções sejam bem-sucedidas ou todas as inserções sejam revertidas.

Introdução

No tutorial Atualização em Lote , examinamos a personalização do controle GridView para apresentar uma interface em que vários registros eram editáveis. O usuário que visita a página pode fazer uma série de alterações e, com um único clique de botão, executar uma atualização em lote. Para situações em que os usuários normalmente atualizam muitos registros de uma só vez, essa interface pode salvar inúmeros cliques e opções de contexto de teclado para mouse em comparação com os recursos padrão de edição por linha que foram explorados pela primeira vez no tutorial Uma Visão geral de inserção, atualização e exclusão de dados .

Esse conceito também pode ser aplicado ao adicionar registros. Imagine que aqui na Northwind Traders normalmente recebemos remessas de fornecedores que contêm vários produtos para uma determinada categoria. Por exemplo, podemos receber uma remessa de seis produtos diferentes de chá e café da Tokyo Traders. Se um usuário inserir os seis produtos um de cada vez por meio de um controle DetailsView, ele terá que escolher muitos dos mesmos valores repetidamente: ele precisará escolher a mesma categoria (Bebidas), o mesmo fornecedor (Tokyo Traders), o mesmo valor descontinuado (False) e as mesmas unidades no valor do pedido (0). Essa entrada de dados repetitiva não é apenas demorada, mas é propensa a erros.

Com um pouco de trabalho, podemos criar uma interface de inserção em lote que permite que o usuário escolha o fornecedor e a categoria uma vez, insira uma série de nomes de produtos e preços unitários e clique em um botão para adicionar os novos produtos ao banco de dados (consulte a Figura 1). À medida que cada produto é adicionado, seus ProductName campos de dados e UnitPrice são atribuídos aos valores inseridos em TextBoxes, enquanto seus CategoryID valores e SupplierID são atribuídos aos valores de DropDownLists na parte superior do formulário. Os Discontinued valores e UnitsOnOrder são definidos como os valores embutidos em código de False e 0, respectivamente.

A interface de inserção em lote

Figura 1: a interface de inserção em lote (clique para exibir a imagem em tamanho real)

Neste tutorial, criaremos uma página que implementa a interface de inserção em lote mostrada na Figura 1. Assim como nos dois tutoriais anteriores, encapsularemos as inserções dentro do escopo de uma transação para garantir a atomicidade. Vamos começar!

Etapa 1: Criando a interface de exibição

Este tutorial consistirá em uma única página dividida em duas regiões: uma região de exibição e uma região de inserção. A interface de exibição, que criaremos nesta etapa, mostra os produtos em um GridView e inclui um botão intitulado Processar Remessa de Produtos. Quando esse botão é clicado, a interface de exibição é substituída pela interface de inserção, que é mostrada na Figura 1. A interface de exibição retorna depois que os botões Adicionar Produtos de Remessa ou Cancelar são clicados. Criaremos a interface de inserção na Etapa 2.

Ao criar uma página que tenha duas interfaces, apenas uma das quais é visível por vez, cada interface normalmente é colocada dentro de um controle Da Web do Painel, que serve como um contêiner para outros controles. Portanto, nossa página terá dois controles panel um para cada interface.

Comece abrindo a BatchInsert.aspx página na BatchData pasta e arraste um Painel da Caixa de Ferramentas para o Designer (consulte a Figura 2). Defina a propriedade Do ID painel como DisplayInterface. Ao adicionar o Painel ao Designer, suas Height propriedades e Width são definidas como 50px e 125px, respectivamente. Limpe esses valores de propriedade do janela Propriedades.

Arraste um Painel da Caixa de Ferramentas para o Designer

Figura 2: Arraste um Painel da Caixa de Ferramentas para o Designer (Clique para exibir a imagem em tamanho real)

Em seguida, arraste um controle Button e GridView para o Painel. Defina a propriedade Button s ID como ProcessShipment e sua Text propriedade como Processar Remessa de Produto. Defina a propriedade gridView como ID e, de sua marca inteligente, associe-a a um novo ObjectDataSource chamado ProductsDataSource.ProductsGrid Configure o ObjectDataSource para efetuar pull de seus dados do ProductsBLL método da classe s GetProducts . Como esse GridView é usado apenas para exibir dados, defina as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum). Clique em Concluir para concluir o assistente Configurar Fonte de Dados.

Exibir os dados retornados do método GetProducts da classe ProductsBLL

Figura 3: Exibir os dados retornados do ProductsBLL método da GetProducts classe (clique para exibir a imagem em tamanho real)

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 ObjectDataSource, o Visual Studio adicionará BoundFields e um CheckBoxField para os campos de dados do produto. Remova todos, exceto os ProductNamecampos , CategoryName, SupplierNameUnitPrice, e Discontinued . Sinta-se à vontade para fazer personalizações estéticas. Decidi formatar o UnitPrice campo como um valor de moeda, reordenei os campos e renomeei vários dos valores de campos HeaderText . Configure também o GridView para incluir paginação e suporte à classificação marcando as caixas de seleção Habilitar Paginação e Habilitar Classificação na marca inteligente GridView.

Depois de adicionar os controles Panel, Button, GridView e ObjectDataSource e personalizar os campos do GridView, a marcação declarativa da página deve ser semelhante à seguinte:

<asp:Panel ID="DisplayInterface" runat="server">
    <p>
        <asp:Button ID="ProcessShipment" runat="server" 
            Text="Process Product Shipment" /> 
    </p>
    <asp:GridView ID="ProductsGrid" runat="server" AllowPaging="True" 
        AllowSorting="True" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
        <Columns>
            <asp:BoundField DataField="ProductName" HeaderText="Product" 
                SortExpression="ProductName" />
            <asp:BoundField DataField="CategoryName" HeaderText="Category" 
                ReadOnly="True" SortExpression="CategoryName" />
            <asp:BoundField DataField="SupplierName" HeaderText="Supplier" 
                ReadOnly="True" SortExpression="SupplierName" />
            <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" 
                HeaderText="Price" HtmlEncode="False" 
                SortExpression="UnitPrice">
                <ItemStyle HorizontalAlign="Right" />
            </asp:BoundField>
            <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" 
                SortExpression="Discontinued">
                <ItemStyle HorizontalAlign="Center" />
            </asp:CheckBoxField>
        </Columns>
    </asp:GridView>
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
</asp:Panel>

Observe que a marcação para Button e GridView aparece dentro das marcas de abertura e fechamento <asp:Panel> . Como esses controles estão dentro do Painel, podemos ocultá-los DisplayInterface simplesmente definindo a propriedade do Visible Painel como False. A Etapa 3 analisa a alteração programática da propriedade do Visible Painel em resposta a um clique de botão para mostrar uma interface enquanto oculta a outra.

Reserve um momento para ver nosso progresso por meio de um navegador. Como mostra a Figura 5, você deve ver um botão Processar Remessa de Produto acima de um GridView que lista os produtos dez de cada vez.

O GridView Listas os recursos de classificação e paginação de produtos e ofertas

Figura 5: o GridView Listas os recursos de classificação e paginação de produtos e ofertas (clique para exibir a imagem em tamanho real)

Etapa 2: Criando a interface de inserção

Com a interface de exibição concluída, estamos prontos para criar a interface de inserção. Para este tutorial, vamos criar uma interface de inserção que solicita um único valor de fornecedor e categoria e, em seguida, permite que o usuário insira até cinco nomes de produtos e valores de preço unitário. Com essa interface, o usuário pode adicionar um a cinco novos produtos que compartilham a mesma categoria e fornecedor, mas têm nomes e preços exclusivos de produtos.

Comece arrastando um Painel da Caixa de Ferramentas para o Designer, colocando-o abaixo do Painel existenteDisplayInterface. Defina a ID propriedade desse Painel recém-adicionado como InsertingInterface e defina sua propriedade Falsecomo Visible . Adicionaremos código que define a InsertingInterface propriedade do Visible Painel como True na Etapa 3. Desmarque também os valores da propriedade e do HeightWidth Painel.

Em seguida, precisamos criar a interface de inserção que foi mostrada de volta na Figura 1. Essa interface pode ser criada por meio de uma variedade de técnicas HTML, mas usaremos uma bastante simples: uma tabela de quatro colunas e sete linhas.

Observação

Ao inserir marcação para elementos HTML <table> , prefiro usar o modo de exibição Origem. Embora o Visual Studio tenha ferramentas para adicionar <table> elementos por meio do Designer, o Designer parece muito disposto a injetar sem assistência para style configurações na marcação. Depois de criar a <table> marcação, geralmente retorno ao Designer para adicionar os controles da Web e definir suas propriedades. Ao criar tabelas com colunas e linhas predeterminadas, prefiro usar HTML estático em vez do controle Da Web de Tabela porque todos os controles da Web colocados em um controle Da Web de Tabela só podem ser acessados usando o FindControl("controlID") padrão . No entanto, uso controles da Web de Tabela para tabelas de tamanho dinâmico (aqueles cujas linhas ou colunas são baseadas em alguns critérios especificados pelo banco de dados ou pelo usuário), uma vez que o controle Da Web de Tabela pode ser construído programaticamente.

Insira a seguinte marcação dentro das <asp:Panel> marcas do InsertingInterface Painel:

<table class="DataWebControlStyle" cellspacing="0">
    <tr class="BatchInsertHeaderRow">
        <td class="BatchInsertLabel">Supplier:</td>
        <td></td>
        <td class="BatchInsertLabel">Category:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertAlternatingRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertAlternatingRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertFooterRow">
        <td colspan="4">
        </td>
    </tr>
</table>

Essa <table> marcação ainda não inclui nenhum controle da Web, vamos adicioná-los momentaneamente. Observe que cada <tr> elemento contém uma configuração de classe CSS específica: BatchInsertHeaderRow para a linha de cabeçalho para a qual o fornecedor e a categoria DropDownLists irão; BatchInsertFooterRow para a linha de rodapé onde os botões Adicionar Produtos de Remessa e Cancelar irão; e alternar BatchInsertRow e BatchInsertAlternatingRow valores para as linhas que conterão os controles TextBox de preço unitário e produto. Criei classes CSS correspondentes no Styles.css arquivo para dar à interface de inserção uma aparência semelhante aos controles GridView e DetailsView que usamos ao longo desses tutoriais. Essas classes CSS são mostradas abaixo.

/*** Styles for ~/BatchData/BatchInsert.aspx tutorial ***/
.BatchInsertLabel
{
    font-weight: bold;
    text-align: right;
}
.BatchInsertHeaderRow td
{
    color: White;
    background-color: #900;
    padding: 11px;
}
.BatchInsertFooterRow td
{
    text-align: center;
    padding-top: 5px;
}
.BatchInsertRow
{
}
.BatchInsertAlternatingRow
{
    background-color: #fcc;
}

Com essa marcação inserida, retorne ao modo design. Isso <table> deve ser mostrado como uma tabela de quatro colunas e sete linhas no Designer, como ilustra a Figura 6.

A interface de inserção é composta por uma tabela de quatro colunas Seven-Row

Figura 6: a interface de inserção é composta por uma tabela de quatro colunas Seven-Row (clique para exibir a imagem em tamanho real)

Agora estamos prontos para adicionar os controles da Web à interface de inserção. Arraste duas DropDownLists da Caixa de Ferramentas para as células apropriadas na tabela um para o fornecedor e outra para a categoria.

Defina a propriedade Suppliers do fornecedor DropDownList como ID e associe-a a um novo ObjectDataSource chamado SuppliersDataSource. Configure o novo ObjectDataSource para recuperar seus dados do SuppliersBLL método da classe s GetSuppliers e defina a lista suspensa da guia UPDATE como (Nenhum). Clique em Concluir para concluir o assistente.

Configurar o ObjectDataSource para usar o método GetSuppliers da classe SuppliersBLL

Figura 7: Configurar o ObjectDataSource para usar o SuppliersBLL método da classe (GetSuppliersclique para exibir a imagem em tamanho real)

Fazer com que o Suppliers DropDownList exiba o CompanyName campo de dados e use o SupplierID campo de dados como seus ListItem valores s.

Exibir o campo de dados CompanyName e usar SupplierID como o valor

Figura 8: Exibir o CompanyName Campo de Dados e Usar SupplierID como o Valor (Clique para exibir a imagem em tamanho real)

Nomeie o segundo DropDownList Categories e associe-o a um novo ObjectDataSource chamado CategoriesDataSource. Configure o CategoriesDataSource ObjectDataSource para usar o CategoriesBLL método da classe s GetCategories ; defina as listas suspensas nas guias UPDATE e DELETE como (Nenhum) e clique em Concluir para concluir o assistente. Por fim, fazer com que o DropDownList exiba o CategoryName campo de dados e use o CategoryID como o valor .

Depois que esses dois DropDownLists tiverem sido adicionados e associados a ObjectDataSources configurados adequadamente, sua tela deverá ser semelhante à Figura 9.

A linha de cabeçalho agora contém os fornecedores e categorias dropDownLists

Figura 9: a linha de cabeçalho agora contém o Suppliers e Categories DropDownLists (clique para exibir a imagem em tamanho real)

Agora precisamos criar as TextBoxes para coletar o nome e o preço de cada novo produto. Arraste um controle TextBox da Caixa de Ferramentas para a Designer para cada uma das cinco linhas de preço e nome do produto. Defina as ID propriedades de TextBoxes como ProductName1, UnitPrice1, ProductName2, UnitPrice2, ProductName3, UnitPrice3e assim por diante.

Adicione um CompareValidator após cada um dos TextBoxes de preço unitário, definindo a ControlToValidate propriedade como o apropriado ID. Defina também a Operator propriedade como GreaterThanEqual, ValueToCompare como 0 e Type como Currency. Essas configurações instruem o CompareValidator a garantir que o preço, se inserido, seja um valor de moeda válido maior ou igual a zero. Defina a Text propriedade como *e ErrorMessage como O preço deve ser maior ou igual a zero. Além disso, omita todos os símbolos de moeda.

Observação

A interface de inserção não inclui nenhum controle RequiredFieldValidator, mesmo que o ProductName campo na tabela de Products banco de dados não permita NULL valores. Isso ocorre porque queremos permitir que o usuário insira até cinco produtos. Por exemplo, se o usuário fornecesse o nome do produto e o preço unitário das três primeiras linhas, deixando as duas últimas linhas em branco, apenas adicionaríamos três novos produtos ao sistema. Como ProductName é necessário, no entanto, precisaremos marcar programaticamente para garantir que, se um preço unitário for inserido, um valor de nome de produto correspondente seja fornecido. Abordaremos esse marcar na Etapa 4.

Ao validar a entrada do usuário, o CompareValidator relatará dados inválidos se o valor contiver um símbolo de moeda. Adicione um $ na frente de cada um dos TextBoxes de preço unitário para servir como uma indicação visual que instrui o usuário a omitir o símbolo de moeda ao inserir o preço.

Por fim, adicione um controle ValidationSummary no InsertingInterface Painel, definindo sua ShowMessageBox propriedade como True e sua ShowSummary propriedade como False. Com essas configurações, se o usuário inserir um valor de preço unitário inválido, um asterisco aparecerá ao lado dos controles TextBox ofensivos e o ValidationSummary exibirá uma caixa de mensagem do lado do cliente que mostra a mensagem de erro especificada anteriormente.

Neste ponto, sua tela deve ser semelhante à Figura 10.

A interface de inserção agora inclui caixas de texto para os nomes e preços dos produtos

Figura 10: a interface de inserção agora inclui caixas de texto para os nomes e preços dos produtos (clique para exibir a imagem em tamanho real)

Em seguida, precisamos adicionar os botões Adicionar Produtos de Remessa e Cancelar à linha do rodapé. Arraste dois controles Button da Caixa de Ferramentas para o rodapé da interface de inserção, definindo as propriedades Buttons ID como e CancelButton e Text propriedades como Adicionar Produtos de Remessa e Cancelar, respectivamente.AddProducts Além disso, defina a CancelButton propriedade do controle como CausesValidationfalse.

Por fim, precisamos adicionar um controle Web label que exibirá status mensagens para as duas interfaces. Por exemplo, quando um usuário adiciona com êxito uma nova remessa de produtos, queremos retornar à interface de exibição e exibir uma mensagem de confirmação. Se, no entanto, o usuário fornecer um preço para um novo produto, mas deixar de fora o nome do produto, precisaremos exibir uma mensagem de aviso, pois o ProductName campo é necessário. Como precisamos que essa mensagem seja exibida para ambas as interfaces, coloque-a na parte superior da página fora dos Painéis.

Arraste um controle Web rótulo da Caixa de Ferramentas para a parte superior da página no Designer. Defina a ID propriedade como StatusLabel, desmarque a Text propriedade e defina as Visible propriedades e EnableViewState como False. Como vimos em tutoriais anteriores, definir a EnableViewState propriedade como False nos permite alterar programaticamente os valores da propriedade Label e tê-los automaticamente reverter de volta para seus padrões no postback subsequente. Isso simplifica o código para mostrar uma mensagem status em resposta a alguma ação do usuário que desaparece no postback subsequente. Por fim, defina a StatusLabel propriedade do controle como CssClass Warning, que é o nome de uma classe CSS definida em Styles.css que exibe o texto em uma fonte grande, itálica, em negrito e vermelha.

A Figura 11 mostra a Designer do Visual Studio depois que o Rótulo foi adicionado e configurado.

Coloque o controle StatusLabel acima dos dois controles de painel

Figura 11: coloque o StatusLabel controle acima dos dois controles de painel (clique para exibir a imagem em tamanho real)

Etapa 3: Alternar entre as interfaces de exibição e inserção

Neste ponto, concluímos a marcação para nossas interfaces de exibição e inserção, mas ainda ficamos com duas tarefas:

  • Alternar entre as interfaces de exibição e inserção
  • Adicionando os produtos na remessa ao banco de dados

Atualmente, a interface de exibição está visível, mas a interface de inserção está oculta. Isso ocorre porque a DisplayInterface propriedade Do Visible Painel é definida True como (o valor padrão), enquanto a InsertingInterface propriedade do Visible Painel é definida Falsecomo . Para alternar entre as duas interfaces, precisamos simplesmente alternar o valor da propriedade de Visible cada controle.

Queremos passar da interface de exibição para a interface de inserção quando o botão Processar Remessa de Produto for clicado. Portanto, crie um manipulador de eventos para este evento button que Click contém o seguinte código:

Protected Sub ProcessShipment_Click(sender As Object, e As EventArgs) _
    Handles ProcessShipment.Click
    DisplayInterface.Visible = False
    InsertingInterface.Visible = True
End Sub

Esse código simplesmente oculta o DisplayInterface Painel e mostra o InsertingInterface Painel.

Em seguida, crie manipuladores de eventos para os controles Adicionar Produtos do Botão de Envio e Cancelar na interface de inserção. Quando um desses Botões é clicado, precisamos reverter de volta para a interface de exibição. Crie Click manipuladores de eventos para ambos os controles Button para que eles chamem ReturnToDisplayInterface, um método que adicionaremos momentaneamente. Além de ocultar o InsertingInterface Painel e mostrar o DisplayInterface Painel, o ReturnToDisplayInterface método precisa retornar os controles da Web ao estado de pré-edição. Isso envolve definir as propriedades DropDownLists SelectedIndex como 0 e limpar as Text propriedades dos controles TextBox.

Observação

Considere o que pode acontecer se não retornarmos os controles ao estado de pré-edição antes de retornarmos à interface de exibição. Um usuário pode clicar no botão Processar Remessa de Produto, inserir os produtos da remessa e, em seguida, clicar em Adicionar Produtos da Remessa. Isso adicionaria os produtos e retornaria o usuário à interface de exibição. Neste ponto, talvez o usuário queira adicionar outra remessa. Ao clicar no botão Processar Remessa de Produto, eles retornariam à interface de inserção, mas as seleções DropDownList e os valores de TextBox ainda seriam preenchidos com seus valores anteriores.

Protected Sub AddProducts_Click(sender As Object, e As EventArgs) _
    Handles AddProducts.Click
    ' TODO: Save the products
    ' Revert to the display interface
    ReturnToDisplayInterface()
End Sub
Protected Sub CancelButton_Click(sender As Object, e As EventArgs) _
    Handles CancelButton.Click
    ' Revert to the display interface
    ReturnToDisplayInterface()
End Sub
Const firstControlID As Integer = 1
Const lastControlID As Integer = 5
Private Sub ReturnToDisplayInterface()
    ' Reset the control values in the inserting interface
    Suppliers.SelectedIndex = 0
    Categories.SelectedIndex = 0
    For i As Integer = firstControlID To lastControlID
        CType(InsertingInterface.FindControl _
            ("ProductName" + i.ToString()), TextBox).Text = String.Empty
        CType(InsertingInterface.FindControl _
            ("UnitPrice" + i.ToString()), TextBox).Text = String.Empty
    Next
    DisplayInterface.Visible = True
    InsertingInterface.Visible = False
End Sub

Ambos os Click manipuladores de eventos simplesmente chamam o ReturnToDisplayInterface método , embora retornemos ao manipulador de eventos Adicionar Produtos da Remessa Click na Etapa 4 e adicionaremos código para salvar os produtos. ReturnToDisplayInterface começa retornando o Suppliers e Categories DropDownLists para suas primeiras opções. As duas constantes firstControlID e lastControlID marcam os valores de índice de controle inicial e final usados na nomenclatura do nome do produto e do preço unitário TextBoxes na interface de inserção e são usados nos limites do For loop que define as Text propriedades dos controles TextBox de volta para uma cadeia de caracteres vazia. Por fim, as propriedades Painéis Visible são redefinidas para que a interface de inserção fique oculta e a interface de exibição seja mostrada.

Tire um momento para testar esta página em um navegador. Ao visitar a página pela primeira vez, você deverá ver a interface de exibição, conforme mostrado na Figura 5. Clique no botão Processar Remessa de Produto. A página será postback e agora você deverá ver a interface de inserção, conforme mostrado na Figura 12. Clicar nos botões Adicionar Produtos de Remessa ou Cancelar retorna você para a interface de exibição.

Observação

Ao exibir a interface de inserção, tire um momento para testar os CompareValidators no preço unitário TextBoxes. Você deverá ver um aviso de caixa de mensagem do lado do cliente ao clicar no botão Adicionar Produtos da Remessa com valores de moeda ou preços inválidos com um valor menor que zero.

A interface de inserção é exibida depois de clicar no botão Processar Remessa de Produto

Figura 12: a interface de inserção é exibida depois de clicar no botão Processar Envio do Produto (Clique para exibir a imagem em tamanho real)

Etapa 4: Adicionar os produtos

Tudo o que resta para este tutorial é salvar os produtos no banco de dados no manipulador de eventos Adicionar Produtos do Click Botão de Remessa. Isso pode ser feito criando um ProductsDataTable e adicionando uma instância para cada um ProductsRow dos nomes de produto fornecidos. Depois que esses ProductsRow s tiverem sido adicionados, faremos uma chamada para o ProductsBLL método da classe passando UpdateWithTransaction no ProductsDataTable. Lembre-se de que o UpdateWithTransaction método , que foi criado novamente no tutorial Encapsulando modificações de banco de dados dentro de uma transação, passa o ProductsDataTable para o ProductsTableAdaptermétodo do .UpdateWithTransaction A partir daí, uma transação ADO.NET é iniciada e o TableAdapter emite uma instrução INSERT para o banco de dados para cada adicionado ProductsRow na DataTable. Supondo que todos os produtos sejam adicionados sem erro, a transação será confirmada, caso contrário, ela será revertida.

O código para o manipulador de eventos Adicionar Produtos do Click Botão de Remessa também precisa executar um pouco de verificação de erro. Como não há RequiredFieldValidators usados na interface de inserção, um usuário pode inserir um preço para um produto ao omitir seu nome. Como o nome do produto é necessário, se essa condição se desenrolar, precisamos alertar o usuário e não prosseguir com as inserções. O código completo Click do manipulador de eventos segue:

Protected Sub AddProducts_Click(sender As Object, e As EventArgs) _
    Handles AddProducts.Click
    ' Make sure that the UnitPrice CompareValidators report valid data...
    If Not Page.IsValid Then Exit Sub
    ' Add new ProductsRows to a ProductsDataTable...
    Dim products As New Northwind.ProductsDataTable()
    For i As Integer = firstControlID To lastControlID
        ' Read in the values for the product name and unit price
        Dim productName As String = CType(InsertingInterface.FindControl _
            ("ProductName" + i.ToString()), TextBox).Text.Trim()
        Dim unitPrice As String = CType(InsertingInterface.FindControl _
            ("UnitPrice" + i.ToString()), TextBox).Text.Trim()
        ' Ensure that if unitPrice has a value, so does productName
        If unitPrice.Length > 0 AndAlso productName.Length = 0 Then
            ' Display a warning and exit this event handler
            StatusLabel.Text = "If you provide a unit price you must also 
                                include the name of the product."
            StatusLabel.Visible = True
            Exit Sub
        End If
        ' Only add the product if a product name value is provided
        If productName.Length > 0 Then
            ' Add a new ProductsRow to the ProductsDataTable
            Dim newProduct As Northwind.ProductsRow = products.NewProductsRow()
            ' Assign the values from the web page
            newProduct.ProductName = productName
            newProduct.SupplierID = Convert.ToInt32(Suppliers.SelectedValue)
            newProduct.CategoryID = Convert.ToInt32(Categories.SelectedValue)
            If unitPrice.Length > 0 Then
                newProduct.UnitPrice = Convert.ToDecimal(unitPrice)
            End If
            ' Add any "default" values
            newProduct.Discontinued = False
            newProduct.UnitsOnOrder = 0
            products.AddProductsRow(newProduct)
        End If
    Next
    ' If we reach here, see if there were any products added
    If products.Count > 0 Then
        ' Add the new products to the database using a transaction
        Dim productsAPI As New ProductsBLL()
        productsAPI.UpdateWithTransaction(products)
        ' Rebind the data to the grid so that the products just added are displayed
        ProductsGrid.DataBind()
        ' Display a confirmation (don't use the Warning CSS class, though)
        StatusLabel.CssClass = String.Empty
        StatusLabel.Text = String.Format( _
            "{0} products from supplier {1} have been " & _
            "added and filed under category {2}.", _
            products.Count, Suppliers.SelectedItem.Text, Categories.SelectedItem.Text)
        StatusLabel.Visible = True
        ' Revert to the display interface
        ReturnToDisplayInterface()
    Else
        ' No products supplied!
        StatusLabel.Text = 
            "No products were added. Please enter the " & _
            "product names and unit prices in the textboxes."
        StatusLabel.Visible = True
    End If
End Sub

O manipulador de eventos começa garantindo que a Page.IsValid propriedade retorne um valor de True. Se retornar False, isso significa que um ou mais dos CompareValidators estão relatando dados inválidos; nesse caso, não queremos tentar inserir os produtos inseridos ou acabaremos com uma exceção ao tentar atribuir o valor de preço unitário inserido pelo usuário à ProductsRow propriedade s UnitPrice .

Em seguida, uma nova ProductsDataTable instância é criada (products). Um For loop é usado para iterar por meio do nome do produto e do preço unitário TextBoxes e as Text propriedades são lidas productName nas variáveis locais e unitPrice. Se o usuário tiver inserido um valor para o preço unitário, mas não para o nome do produto correspondente, o StatusLabel exibirá a mensagem Se você fornecer um preço unitário, também deverá incluir o nome do produto e o manipulador de eventos for encerrado.

Se um nome de produto tiver sido fornecido, uma nova ProductsRow instância será criada usando o ProductsDataTable método s NewProductsRow . Essa nova ProductsRow propriedade de instância é definida como o nome do ProductName produto textBox atual, enquanto as SupplierID propriedades e CategoryID são atribuídas às SelectedValue propriedades do DropDownLists no cabeçalho da interface de inserção. Se o usuário inseriu um valor para o preço do produto, ele será atribuído à ProductsRow propriedade da UnitPrice instância; caso contrário, a propriedade será deixada sem atribuição, o que resultará em um NULL valor para UnitPrice no banco de dados. Por fim, as Discontinued propriedades e UnitsOnOrder são atribuídas aos valores embutidos False em código e 0, respectivamente.

Depois que as propriedades tiverem sido atribuídas à ProductsRow instância, elas serão adicionadas ao ProductsDataTable.

Ao concluir o For loop, marcar se algum produto foi adicionado. Afinal, o usuário pode ter clicado em Adicionar Produtos da Remessa antes de inserir nomes ou preços de produtos. Se houver pelo menos um produto no ProductsDataTable, o ProductsBLL método da classe será UpdateWithTransaction chamado. Em seguida, os dados são recuperados para o ProductsGrid GridView para que os produtos recém-adicionados apareçam na interface de exibição. O StatusLabel é atualizado para exibir uma mensagem de confirmação e o ReturnToDisplayInterface é invocado, ocultando a interface de inserção e mostrando a interface de exibição.

Se nenhum produto tiver sido inserido, a interface de inserção permanecerá exibida, mas a mensagem Nenhum produto foi adicionado. Insira os nomes dos produtos e os preços unitários nas caixas de texto são exibidos.

As figuras 13, 14 e 15 mostram as interfaces de inserção e exibição em ação. Na Figura 13, o usuário inseriu um valor de preço unitário sem um nome de produto correspondente. A Figura 14 mostra a interface de exibição depois que três novos produtos foram adicionados com êxito, enquanto a Figura 15 mostra dois dos produtos recém-adicionados no GridView (o terceiro está na página anterior).

Um nome de produto é necessário ao inserir um preço unitário

Figura 13: Um nome de produto é necessário ao inserir um preço unitário (clique para exibir a imagem em tamanho real)

Três novos vegetais foram adicionados para o fornecedor Mayumi s

Figura 14: Três novos vegetais foram adicionados para o fornecedor Mayumi s (clique para exibir a imagem em tamanho real)

Os novos produtos podem ser encontrados na última página do GridView

Figura 15: Os novos produtos podem ser encontrados na última página do GridView (clique para exibir a imagem em tamanho real)

Observação

A lógica de inserção em lote usada neste tutorial encapsula as inserções no escopo da transação. Para verificar isso, introduza propositalmente um erro no nível do banco de dados. Por exemplo, em vez de atribuir a propriedade da CategoryID nova ProductsRow instância ao valor selecionado no DropDownList, atribua-a Categories a um valor como i * 5. Aqui i está o indexador de loop e tem valores que variam de 1 a 5. Portanto, ao adicionar dois ou mais produtos na inserção em lote, o primeiro produto terá um valor válido CategoryID (5), mas os produtos subsequentes terão CategoryID valores que não correspondem aos CategoryID valores na Categories tabela. O efeito líquido é que, embora o primeiro INSERT seja bem-sucedido, os subsequentes falharão com uma violação de restrição de chave estrangeira. Como a inserção em lote é atômica, a primeira INSERT será revertida, retornando o banco de dados para seu estado antes do início do processo de inserção em lote.

Resumo

Neste e nos dois tutoriais anteriores, criamos interfaces que permitem atualizar, excluir e inserir lotes de dados, todos os quais usaram o suporte à transação que adicionamos à Camada de Acesso a Dados no tutorial Encapsulando modificações de banco de dados em um tutorial de transação . Para determinados cenários, essas interfaces de usuário de processamento em lotes melhoram muito a eficiência do usuário final reduzindo o número de cliques, postbacks e comutadores de contexto de teclado para mouse, mantendo também a integridade dos dados subjacentes.

Este tutorial conclui nossa análise de como trabalhar com dados em lote. O próximo conjunto de tutoriais explora uma variedade de cenários avançados da Camada de Acesso a Dados, incluindo o uso de procedimentos armazenados nos métodos TableAdapter, configurações de nível de conexão e comando no DAL, criptografia de cadeias de conexão e muito mais!

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 Hilton Giesenow e S ren Jacob Lauritsen. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, solte-me uma linha em mitchell@4GuysFromRolla.com.