Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
por Scott Mitchell
Neste tutorial, analisamos os fundamentos do controle de simultaneidade otimista e, em seguida, exploramos como implementá-lo usando o controle SqlDataSource.
Introdução
No tutorial anterior, examinamos como adicionar recursos de inserção, atualização e exclusão ao controle SqlDataSource. Em resumo, para fornecer esses recursos, precisávamos especificar a instrução SQL correspondente em INSERT, UPDATE ou DELETE nas propriedades dos controlos InsertCommand, UpdateCommand ou DeleteCommand, juntamente com os parâmetros apropriados nas coleções InsertParameters, UpdateParameters e DeleteParameters. Embora essas propriedades e coleções possam ser especificadas manualmente, o botão Avançado do assistente Configurar Fonte de Dados oferece uma caixa de seleção para gerar instruções INSERT, UPDATE e DELETE, que irão criar automaticamente essas instruções com base na instrução SELECT.
Junto com a caixa de seleção Gerar INSERT, UPDATEe DELETE instruções, a caixa de diálogo Opções Avançadas de Geração SQL inclui uma opção Usar simultaneidade otimista (consulte a Figura 1). Quando marcadas, as WHERE cláusulas nas instruções UPDATE e DELETE geradas automaticamente são modificadas para apenas realizar a atualização ou eliminação se os dados do banco de dados subjacente não tenham sido alterados desde a última vez que o utilizador carregou os dados na grelha.
Figura 1: Você pode adicionar suporte de simultaneidade otimista na caixa de diálogo Opções avançadas de geração SQL
De volta ao tutorial Implementando simultaneidade otimista, examinamos os fundamentos do controle de simultaneidade otimista e como adicioná-lo ao ObjectDataSource. Neste tutorial, abordaremos os fundamentos do controle de simultaneidade otimista e, em seguida, exploraremos como implementá-lo usando o SqlDataSource.
Um resumo da concorrência otimista
Para aplicações Web que permitem que vários utilizadores simultâneos editem ou eliminem os mesmos dados, existe a possibilidade de um utilizador substituir acidentalmente as alterações de outro. No tutorial Implementação de Simultaneidade Otimista, forneci o seguinte exemplo:
Imagine que dois usuários, Jisun e Sam, estavam visitando uma página em um aplicativo que permitia aos visitantes atualizar e excluir produtos por meio de um controle GridView. Ambos clicam no botão Editar para Chai aproximadamente ao mesmo tempo. Jisun muda o nome do produto para Chai Tea e clica no botão Atualizar. O resultado líquido é uma UPDATE instrução que é enviada para o banco de dados, que define todos os campos atualizáveis do produto (mesmo que Jisun tenha atualizado apenas um campo, ProductName). Neste momento, o banco de dados tem os valores Chai Tea, a categoria Bebidas, o fornecedor Exotic Liquids, e assim por diante para este produto em particular. No entanto, o GridView na tela de Sam ainda mostra o nome do produto na linha editável do GridView como Chai. Alguns segundos após as mudanças de Jisun terem sido confirmadas, Sam atualiza a categoria para Condimentos e clica em Atualizar. Isso resulta numa UPDATE instrução enviada para o banco de dados que define o nome do produto como Chai, o CategoryID ID da categoria correspondente a Condimentos, e assim por diante. As alterações de Jisun no nome do produto foram substituídas.
A Figura 2 ilustra essa interação.
Figura 2: Quando dois usuários atualizam simultaneamente um registro, há potencial para as alterações de um usuário para substituir o outro s (Clique para visualizar a imagem em tamanho real)
Para evitar que este cenário se desenvolva, uma forma de controlo de concorrência deve ser implementada. Simultaneidade otimista O foco deste tutorial trabalha na suposição de que, embora possa haver conflitos de simultaneidade de vez em quando, na grande maioria das vezes tais conflitos não surgirão. Portanto, se surgir um conflito, o controle de simultaneidade otimista simplesmente informa ao usuário que suas alterações não podem ser salvas porque outro usuário modificou os mesmos dados.
Observação
Para aplicações em que se presume que haverá muitos conflitos de simultaneidade ou se tais conflitos não forem toleráveis, então o controle pessimista de simultaneidade pode ser usado em vez disso. Consulte novamente o tutorial Implementando simultaneidade otimista para uma discussão mais aprofundada sobre o controle de simultaneidade pessimista.
O controle de simultaneidade otimista funciona garantindo que o registro que está sendo atualizado ou excluído tenha os mesmos valores que quando o processo de atualização ou exclusão foi iniciado. Por exemplo, ao clicar no botão Editar em um GridView editável, os valores s do registro são lidos do banco de dados e exibidos em TextBoxes e outros controles da Web. Esses valores originais são salvos pelo GridView. Mais tarde, depois que o usuário fizer as alterações e clicar no botão Atualizar, a UPDATE instrução usada deve levar em conta os valores originais mais os novos valores e só atualizar o registro do banco de dados subjacente se os valores originais que o usuário começou a editar forem idênticos aos valores ainda no banco de dados. A Figura 3 mostra esta sequência de eventos.
Figura 3: Para que a atualização ou exclusão seja bem-sucedida, os valores originais devem ser iguais aos valores atuais do banco de dados (Clique para visualizar a imagem em tamanho real)
Existem várias abordagens para implementar simultaneidade otimista (veja a lógica de atualização da simultaneidade otimista de Peter A. Bromberg para uma breve análise de um número de opções). A técnica usada pelo SqlDataSource (bem como pelo ADO.NET Typed DataSets usado em nossa camada de acesso a dados) aumenta a WHERE cláusula para incluir uma comparação de todos os valores originais. A instrução a seguir UPDATE , por exemplo, atualiza o nome e o preço de um produto somente se os valores atuais do banco de dados forem iguais aos valores que foram originalmente recuperados ao atualizar o registro no GridView. Os @ProductName parâmetros e @UnitPrice contêm os novos valores inseridos pelo usuário, enquanto @original_ProductName e @original_UnitPrice contêm os valores que foram originalmente carregados no GridView quando o botão Editar foi clicado:
UPDATE Products SET
ProductName = @ProductName,
UnitPrice = @UnitPrice
WHERE
ProductID = @original_ProductID AND
ProductName = @original_ProductName AND
UnitPrice = @original_UnitPrice
Como veremos neste tutorial, habilitar o controle de simultaneidade otimista com o SqlDataSource é tão simples quanto marcar uma caixa de seleção.
Etapa 1: Criando um SqlDataSource que suporta simultaneidade otimista
Comece por abrir a página OptimisticConcurrency.aspx a partir da pasta SqlDataSource. Arraste um controle SqlDataSource da Caixa de Ferramentas para o Designer, define sua ID propriedade como ProductsDataSourceWithOptimisticConcurrency. Em seguida, clique no link Configurar a fonte de dados da etiqueta inteligente do controlo. Na primeira tela do assistente, escolha trabalhar com o NORTHWINDConnectionString e clique em Avançar.
Figura 4: Escolha trabalhar com o NORTHWINDConnectionString (Clique para visualizar a imagem em tamanho real)
Para este exemplo, adicionaremos um GridView que permite que os usuários editem a Products tabela. Portanto, na tela "Configurar a Declaração de Seleção", escolha a tabela Products na lista suspensa e selecione as colunas ProductID, ProductName, UnitPrice e Discontinued, conforme mostrado na Figura 5.
Figura 5: Na tabela, retorne as colunas Products, ProductID, ProductName e UnitPrice (Discontinued)
Depois de selecionar as colunas, clique no botão Avançado para abrir a caixa de diálogo Opções Avançadas de Geração SQL. Verifique as instruções Gerar INSERT, UPDATE, e DELETE e as caixas de seleção Usar simultaneidade otimista e clique em OK (consulte a Figura 1 para uma captura de ecrã). Conclua o assistente clicando em Avançar e, em seguida, em Concluir.
Depois de concluir o assistente Configurar Fonte de Dados, reserve um momento para examinar as propriedades DeleteCommand e UpdateCommand e as coleções DeleteParameters e UpdateParameters. A maneira mais fácil de fazer isso é clicar na guia Fonte no canto inferior esquerdo para ver a sintaxe declarativa da página. Ali encontrará um UpdateCommand valor de:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
A coleção UpdateParameters contém sete parâmetros.
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
runat="server" ...>
<DeleteParameters>
...
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="UnitPrice" Type="Decimal" />
<asp:Parameter Name="Discontinued" Type="Boolean" />
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</UpdateParameters>
...
</asp:SqlDataSource>
Da mesma forma, a DeleteCommand propriedade e a coleção DeleteParameters devem ter a seguinte aparência:
DELETE FROM [Products]
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
runat="server" ...>
<DeleteParameters>
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</DeleteParameters>
<UpdateParameters>
...
</UpdateParameters>
...
</asp:SqlDataSource>
Além de aumentar as WHERE cláusulas das propriedades UpdateCommand e DeleteCommand (e adicionar os parâmetros adicionais às respetivas coleções de parâmetros), selecionar a opção Usar concorrência otimista ajusta duas outras propriedades.
- Altera a
ConflictDetectionpropriedade deOverwriteChanges(o padrão) paraCompareAllValues - Altera a
OldValuesParameterFormatStringpropriedade de {0} (o padrão) para original_{0} .
Quando o controle Web de dados invoca os métodos do SqlDataSource Update() ou Delete(), ele passa os valores originais. Se a propriedade s ConflictDetection SqlDataSource estiver definida como CompareAllValues, esses valores originais serão adicionados ao comando. A OldValuesParameterFormatString propriedade fornece o padrão de nomenclatura usado para esses parâmetros de valor original. O assistente Configurar Fonte de Dados usa original_{0} e nomeia cada parâmetro original nas propriedades UpdateCommand, DeleteCommand e coleções UpdateParameters, DeleteParameters conforme apropriado.
Observação
Como não estamos usando os recursos de inserção do controle SqlDataSource, pode remover à vontade a InsertCommand propriedade e sua InsertParameters coleção.
Manipulação correta deNULLvalores
Infelizmente, as instruções aumentadas UPDATE e DELETE geradas automaticamente pelo assistente Configurar Fonte de Dados ao usar simultaneidade otimista não funcionam com registros que contêm NULL valores. Para ver o porquê, considere nossos SqlDataSource s UpdateCommand:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
A UnitPrice coluna na Products tabela pode ter NULL valores. Se um registro específico tiver um NULL valor para UnitPrice, a parte WHERE da [UnitPrice] = @original_UnitPrice cláusula sempre será avaliada como False porque NULL = NULL sempre retorna False. Portanto, os registros que contêm NULL valores não podem ser editados ou excluídos, pois as UPDATE cláusulas e DELETE instruções WHERE não retornarão nenhuma linha para atualizar ou excluir.
Observação
Este bug foi relatado pela primeira vez à Microsoft em junho de 2004 em SqlDataSource gera instruções SQL incorretas e está supostamente programado para ser corrigido na próxima versão do ASP.NET.
Para corrigir isto, temos de atualizar manualmente as cláusulas WHERE nas propriedades UpdateCommand e DeleteCommand para todas as colunas que possam ter valores . Em geral, mude [ColumnName] = @original_ColumnName para:
(
([ColumnName] IS NULL AND @original_ColumnName IS NULL)
OR
([ColumnName] = @original_ColumnName)
)
Essa modificação pode ser feita diretamente por meio da marcação declarativa, por meio das opções UpdateQuery ou DeleteQuery da janela Propriedades, ou por meio das guias UPDATE e DELETE na opção Especificar uma instrução SQL personalizada ou procedimento armazenado no assistente Configurar Fonte de Dados. Novamente, essa modificação deve ser feita para cada coluna nas cláusulas UpdateCommand e DeleteCommandWHERE que podem conter valores de NULL.
A aplicação deste ao nosso exemplo resulta nos seguintes valores modificados UpdateCommandDeleteCommand :
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
(([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice)) AND
[Discontinued] = @original_Discontinued
DELETE FROM [Products]
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
(([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice)) AND
[Discontinued] = @original_Discontinued
Etapa 2: Adicionando um GridView com opções de edição e exclusão
Com o SqlDataSource configurado para oferecer suporte à simultaneidade otimista, tudo o que resta é adicionar um controle da Web de dados à página que utiliza esse controle de simultaneidade. Para este tutorial, vamos adicionar um GridView que fornece funcionalidade de edição e exclusão. Para fazer isso, arraste um GridView da Caixa de Ferramentas para o Designer e defina o seu ID para Products. A partir do rótulo inteligente do GridView, vincule-o ao ProductsDataSourceWithOptimisticConcurrency controle SqlDataSource adicionado na Etapa 1. Por fim, marque as opções Ativar edição e Ativar eliminação da etiqueta inteligente.
Figura 6: Vincular o GridView ao SqlDataSource e habilitar a edição e a exclusão (Clique para visualizar a imagem em tamanho real)
Depois de adicionar o GridView, configure a sua aparência removendo o ProductID BoundField, alterando a propriedade de ProductName BoundField para Produto e atualizando o HeaderText BoundField para que a sua propriedade seja simplesmente Preço. Idealmente, melhoramos a interface de edição para incluir um RequiredFieldValidator para o ProductName valor e um CompareValidator para o UnitPrice valor (para garantir que seja um valor numérico devidamente formatado). Consulte o tutorial Personalizando a interface de modificação de dados para obter uma visão mais detalhada da personalização da interface de edição do GridView.
Observação
O estado de exibição de GridView deve ser habilitado, pois os valores originais passados do GridView para o SqlDataSource são armazenados no estado de exibição.
Depois de fazer essas modificações no GridView, a marcação declarativa GridView e SqlDataSource deve ser semelhante à seguinte:
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
runat="server" ConflictDetection="CompareAllValues"
ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
DeleteCommand=
"DELETE FROM [Products]
WHERE [ProductID] = @original_ProductID
AND [ProductName] = @original_ProductName
AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice))
AND [Discontinued] = @original_Discontinued"
OldValuesParameterFormatString=
"original_{0}"
SelectCommand=
"SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
FROM [Products]"
UpdateCommand=
"UPDATE [Products]
SET [ProductName] = @ProductName, [UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE [ProductID] = @original_ProductID
AND [ProductName] = @original_ProductName
AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice))
AND [Discontinued] = @original_Discontinued">
<DeleteParameters>
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="UnitPrice" Type="Decimal" />
<asp:Parameter Name="Discontinued" Type="Boolean" />
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</UpdateParameters>
</asp:SqlDataSource>
<asp:GridView ID="Products" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceWithOptimisticConcurrency">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" HeaderText="Price"
SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
Para ver o controle de simultaneidade otimista em ação, abra duas janelas do navegador e carregue a página OptimisticConcurrency.aspx em ambas. Clique nos botões Editar para o primeiro produto em ambos os navegadores. Em um navegador, altere o nome do produto e clique em Atualizar. O navegador fará o postback e o GridView retornará ao seu modo de pré-edição, mostrando o novo nome do produto para o registro recém-editado.
Na segunda janela do navegador, altere o preço (mas deixe o nome do produto como seu valor original) e clique em Atualizar. No postback, a grade retorna ao seu modo de pré-edição, mas a alteração no preço não é registrada. O segundo navegador mostra o mesmo valor que o primeiro, o nome do novo produto com o preço antigo. As alterações feitas na segunda janela do navegador foram perdidas. Além disso, as alterações foram perdidas de forma bastante silenciosa, pois não havia nenhuma exceção ou mensagem indicando que uma violação de simultaneidade acabou de ocorrer.
Figura 7: As alterações na segunda janela do navegador foram silenciosamente perdidas (Clique para visualizar a imagem em tamanho real)
A razão pela qual as alterações do segundo navegador não foram confirmadas foi porque a cláusula da instrução UPDATEWHERE filtrou todos os registos e, portanto, não afetou nenhuma linha. Vejamos novamente a UPDATE afirmação:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
(([UnitPrice] IS NULL AND @original_UnitPrice IS NULL) OR
([UnitPrice] = @original_UnitPrice)) AND
[Discontinued] = @original_Discontinued
Quando a segunda janela do navegador atualiza o registro, o nome do produto original especificado na WHERE cláusula não corresponde ao nome do produto existente (uma vez que foi alterado pelo primeiro navegador). Portanto, a instrução [ProductName] = @original_ProductName retorna False, e o UPDATE não afeta nenhum registro.
Observação
Excluir funciona da mesma maneira. Com duas janelas do navegador abertas, comece editando um determinado produto com uma e, em seguida, salve suas alterações. Depois de salvar as alterações em um navegador, clique no botão Excluir para o mesmo produto no outro. Como os valores originais não correspondem na cláusula DELETE da instrução WHERE, a eliminação falha silenciosamente.
Do ponto de vista do usuário final na segunda janela do navegador, depois de clicar no botão Atualizar, a grade retorna ao modo de pré-edição, mas suas alterações foram perdidas. No entanto, não há feedback visual de que suas alterações não se mantiveram. Idealmente, se as alterações de um usuário forem perdidas por uma violação de simultaneidade, nós o notificaremos e, talvez, manteremos a grade no modo de edição. Vamos ver como conseguir isso.
Etapa 3: Como determinar quando ocorreu uma violação de concorrência
Uma vez que uma violação de simultaneidade rejeita as alterações feitas, seria bom alertar o usuário quando uma violação de simultaneidade ocorreu. Para alertar o usuário, deixe s adicionar um controle Web Label na parte superior da página nomeada ConcurrencyViolationMessage cuja Text propriedade exibe a seguinte mensagem: Você tentou atualizar ou excluir um registro que foi atualizado simultaneamente por outro usuário. Reveja as alterações do outro utilizador e, em seguida, refaça a sua atualização ou elimine. Defina a propriedade do controlo Label s CssClass para Warning, uma classe CSS definida em Styles.css que exibe texto em fonte vermelha, itálica, negrito e grande. Por fim, defina as propriedades de Rótulos Visible e EnableViewState como False. Isso ocultará o Rótulo, exceto apenas para os postbacks em que definimos explicitamente sua Visible propriedade como True.
Figura 8: Adicionar um controle de rótulo à página para exibir o aviso (Clique para visualizar a imagem em tamanho real)
Ao realizar uma atualização ou exclusão, os manipuladores de eventos do GridView RowUpdated e RowDeleted são acionados após o controlo da fonte de dados realizar a atualização ou exclusão solicitada. Podemos determinar quantas linhas foram afetadas pela operação a partir desses manipuladores de eventos. Se zero linhas foram afetadas, queremos exibir a ConcurrencyViolationMessage Etiqueta.
Crie um manipulador de eventos para os RowUpdated eventos e RowDeleted e adicione o seguinte código:
Protected Sub Products_RowUpdated(sender As Object, e As GridViewUpdatedEventArgs) _
Handles Products.RowUpdated
If e.AffectedRows = 0 Then
ConcurrencyViolationMessage.Visible = True
e.KeepInEditMode = True
' Rebind the data to the GridView to show the latest changes
Products.DataBind()
End If
End Sub
Protected Sub Products_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
Handles Products.RowDeleted
If e.AffectedRows = 0 Then
ConcurrencyViolationMessage.Visible = True
End If
End Sub
Em ambos os manipuladores de eventos, verificamos a e.AffectedRows propriedade e, se for igual a 0, definimos a ConcurrencyViolationMessage propriedade Label s Visible como True. No manipulador de RowUpdated eventos, também instruímos o GridView a permanecer no modo de edição definindo a KeepInEditMode propriedade como true. Ao fazer isso, precisamos revincular os dados à grade para que os dados do outro usuário sejam carregados na interface de edição. Isso é feito chamando o método s GridView DataBind() .
Como mostra a Figura 9, com esses dois manipuladores de eventos, uma mensagem muito percetível é exibida sempre que ocorre uma violação de simultaneidade.
Figura 9: Uma mensagem é exibida em face de uma violação de simultaneidade (Clique para visualizar a imagem em tamanho real)
Resumo
Ao criar um aplicativo Web em que vários usuários simultâneos podem estar editando os mesmos dados, é importante considerar opções de controle de simultaneidade. Por padrão, os controles da Web de dados ASP.NET e os controles da fonte de dados não empregam nenhum controle de simultaneidade. Como vimos neste tutorial, implementar o controle de simultaneidade otimista com o SqlDataSource é relativamente rápido e fácil. O SqlDataSource lida com a maior parte do trabalho pesado ao adicionar cláusulas aumentadas WHERE às instruções UPDATE e DELETE geradas automaticamente, mas há algumas sutilezas na manipulação de colunas de valores NULL, conforme discutido na seção Manipulação Correta de NULL Valores.
Este tutorial conclui nosso exame do SqlDataSource. Nossos tutoriais restantes voltarão a trabalhar com dados usando o ObjectDataSource e a arquitetura hierárquica.
Feliz Programação!
Sobre o Autor
Scott Mitchell, autor de sete livros sobre ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias Web da Microsoft desde 1998. Scott trabalha como consultor, formador e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Ele pode ser contatado em mitchell@4GuysFromRolla.com.