Implementar a simultaneidade otimista com o SqlDataSource (C#)
por Scott Mitchell
Neste tutorial, examinamos os conceitos básicos 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 , ou correspondente nas propriedades , UpdateCommand
InsertCommand
ou DeleteCommand
do controle, juntamente com os parâmetros apropriados nas InsertParameters
coleções , UpdateParameters
e DeleteParameters
.INSERT
DELETE
UPDATE
Embora essas propriedades e coleções possam ser especificadas manualmente, o botão Avançado do Assistente para Configurar Fonte de Dados oferece uma caixa de seleção Gerar INSERT
instruções , UPDATE
e DELETE
que criará automaticamente essas instruções com base na SELECT
instrução .
Juntamente com a caixa de seleção Gerar INSERT
instruções , UPDATE
e DELETE
, a caixa de diálogo Opções Avançadas de Geração de SQL inclui uma opção Usar simultaneidade otimista (consulte a Figura 1). Quando verificadas, as WHERE
cláusulas nas instruções e DELETE
geradas UPDATE
automaticamente são modificadas para executar apenas a atualização ou exclusão se os dados de banco de dados subjacentes não tiverem sido modificados desde a última vez que o usuário carregou os dados na grade.
Figura 1: Você pode adicionar suporte de simultaneidade otimista na caixa de diálogo Opções avançadas de geração de SQL
De volta ao tutorial Implementando simultaneidade otimista , examinamos os conceitos básicos do controle de simultaneidade otimista e como adicioná-lo ao ObjectDataSource. Neste tutorial, examinaremos novamente os conceitos básicos do controle de simultaneidade otimista e, em seguida, exploraremos como implementá-lo usando o SqlDataSource.
Uma recapitulação da simultaneidade otimista
Para aplicativos Web que permitem que vários usuários simultâneos editem ou excluam os mesmos dados, existe a possibilidade de um usuário substituir acidentalmente outras alterações. No tutorial Implementando 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 ao mesmo tempo. Jisun altera o nome do produto para Chai Tea e clica no botão Atualizar. O resultado líquido é uma instrução UPDATE
enviada ao banco de dados, que define todos os campos atualizáveis do produto (embora o Jisun tenha atualizado apenas um campo, ProductName
). Neste momento, o banco de dados tem os valores Chai Tea, a categoria Bebidas, o fornecedor Liquids Exóticos e assim por diante para este produto específico. No entanto, a tela GridView no Sam ainda mostra o nome do produto na linha GridView editável como Chai. Alguns segundos após a confirmação das alterações do Jisun, Sam atualiza a categoria para Condimentos e clica em Atualizar. Isso resulta em uma instrução UPDATE
enviada ao banco de dados que define o nome do produto como Chai, o CategoryID
para a ID da categoria condimentos correspondente 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 que as alterações de um usuário substituam os outros s (clique para exibir a imagem em tamanho real)
Para evitar que esse cenário se desenvolva, uma forma de controle de simultaneidade deve ser implementada. Simultaneidade otimista O foco deste tutorial funciona na suposição de que, embora possa haver conflitos de simultaneidade de vez em quando, a grande maioria das vezes esses conflitos não surgirão. Portanto, se um conflito surgir, o controle de simultaneidade otimista simplesmente informará ao usuário que suas alterações não podem ser salvas porque outro usuário modificou os mesmos dados.
Observação
Para aplicativos em que supõe-se que haverá muitos conflitos de simultaneidade ou se esses conflitos não forem toleráveis, o controle de simultaneidade pessimista poderá ser usado. Consulte o tutorial Implementando simultaneidade otimista para obter uma discussão mais completa 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 de registro s são lidos do banco de dados e exibidos em TextBoxes e outros controles da Web. Esses valores originais são salvos pelo GridView. Posteriormente, depois que o usuário fizer suas alterações e clicar no botão Atualizar, a UPDATE
instrução usada deverá levar em conta os valores originais mais os novos valores e atualizar apenas o registro de 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 ilustra essa 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 exibir a imagem em tamanho real)
Há várias abordagens para implementar a simultaneidade otimista (consulte a Lógica de Atualização de Simultaneidade Otimista de Peter A. Bromberg para obter uma breve olhada em várias opções). A técnica usada pelo SqlDataSource (bem como pelo ADO.NET Conjuntos de Dados Tipados usados 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 dá suporte à simultaneidade otimista
Comece abrindo a OptimisticConcurrency.aspx
página da SqlDataSource
pasta. Arraste um controle SqlDataSource da Caixa de Ferramentas para o Designer, definindo sua ID
propriedade como ProductsDataSourceWithOptimisticConcurrency
. Em seguida, clique no link Configurar Fonte de Dados da marca inteligente do controle. Na primeira tela do assistente, escolha trabalhar com o NORTHWINDConnectionString
e clique em Avançar.
Figura 4: Optar por Trabalhar com o NORTHWINDConnectionString
(Clique para exibir a imagem em tamanho real)
Neste exemplo, adicionaremos um GridView que permite aos usuários editar a Products
tabela. Portanto, na tela Configurar a Instrução Select, escolha a Products
tabela na lista suspensa e selecione as ProductID
colunas , ProductName
, UnitPrice
e Discontinued
, conforme mostrado na Figura 5.
Figura 5: Na tabela, retorne as Products
ProductID
colunas , ProductName
UnitPrice
, e Discontinued
(clique para exibir a imagem em tamanho real)
Depois de escolher as colunas, clique no botão Avançado para abrir a caixa de diálogo Opções Avançadas de Geração de SQL. Marque as caixas de seleção Gerar INSERT
instruções , UPDATE
e DELETE
Usar simultaneidade otimista e clique em OK (consulte a Figura 1 para obter uma captura de tela). Conclua o assistente clicando em Avançar e em Concluir.
Depois de concluir o assistente Configurar Fonte de Dados, reserve um momento para examinar as propriedades e resultantes DeleteCommand
e as DeleteParameters
coleções e UpdateParameters
.UpdateCommand
A maneira mais fácil de fazer isso é clicar na guia Origem no canto inferior esquerdo para ver a sintaxe declarativa da página. Lá, você 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
Com sete parâmetros na UpdateParameters
coleção:
<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 propriedade e DeleteParameters
a DeleteCommand
coleção devem ser semelhantes às seguintes:
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 UpdateCommand
propriedades e DeleteCommand
(e adicionar os parâmetros adicionais às respectivas coleções de parâmetros), selecionar a opção Usar simultaneidade otimista ajusta duas outras propriedades:
- Altera a
ConflictDetection
propriedade deOverwriteChanges
(o padrão) paraCompareAllValues
- Altera a
OldValuesParameterFormatString
propriedade de {0} (o padrão) para original_{0} .
Quando o controle da Web de dados invoca o método sqlDataSource s Update()
ou Delete()
, ele passa os valores originais. Se a propriedade s ConflictDetection
SqlDataSource estiver definida CompareAllValues
como , esses valores originais serão adicionados ao comando . A OldValuesParameterFormatString
propriedade fornece o padrão de nomenclatura usado para esses parâmetros de valor originais. O assistente Configurar Fonte de Dados usa original_{0} e nomeia cada parâmetro original nas UpdateCommand
propriedades e e UpdateParameters
coleções e DeleteCommand
DeleteParameters
adequadamente.
Observação
Como não estamos usando os recursos de inserção do controle SqlDataSource, fique à vontade para remover a InsertCommand
propriedade e sua InsertParameters
coleção.
ManipulandoNULL
valores corretamente
Infelizmente, as instruções e aumentadas UPDATE
DELETE
geradas automaticamente pelo assistente Configurar Fonte de Dados ao usar a simultaneidade otimista não funcionam com registros que contêm NULL
valores. Para ver por que, considere nosso 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 da [UnitPrice] = @original_UnitPrice
WHERE
cláusula sempre será avaliada como False porque NULL = NULL
sempre retornará False. Portanto, os registros que contêm NULL
valores não podem ser editados ou excluídos, pois as UPDATE
cláusulas WHERE
de instruções e DELETE
não retornarão nenhuma linha a ser atualizada ou excluída.
Observação
Esse bug foi relatado pela primeira vez à Microsoft em junho de 2004 no SqlDataSource gera instruções SQL incorretas e está supostamente agendado para ser corrigido na próxima versão do ASP.NET.
Para corrigir isso, precisamos atualizar manualmente as WHERE
cláusulas nas UpdateCommand
propriedades e DeleteCommand
para todas as colunas que podem ter NULL
valores. Em geral, altere [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 do 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 na UpdateCommand
cláusula e DeleteCommand
s WHERE
que podem conter NULL
valores.
Aplicar isso ao nosso exemplo resulta nos seguintes valores modificados UpdateCommand
e DeleteCommand
:
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: Adicionar um GridView com opções de edição e exclusão
Com o SqlDataSource configurado para dar 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 a funcionalidade de edição e exclusão. Para fazer isso, arraste um GridView da Caixa de Ferramentas para o Designer e defina como ID
Products
. Na marca inteligente GridView, associe-a ProductsDataSourceWithOptimisticConcurrency
ao controle SqlDataSource adicionado na Etapa 1. Por fim, marcar as opções Habilitar Edição e Habilitar Exclusão da marca inteligente.
Figura 6: Associar o GridView ao SqlDataSource e Habilitar Edição e Exclusão (Clique para exibir a imagem em tamanho real)
Depois de adicionar o GridView, configure sua aparência removendo o ProductID
BoundField, alterando a ProductName
propriedade BoundField para HeaderText
Product e atualizando o UnitPrice
BoundField para que sua HeaderText
propriedade seja simplesmente Price. O ideal é aprimorar a interface de edição para incluir um RequiredFieldValidator para o ProductName
valor e um CompareValidator para o UnitPrice
valor (para garantir que ele seja um valor numérico formatado corretamente). Consulte o tutorial Personalizando a Interface de Modificação de Dados para obter uma visão mais detalhada sobre como personalizar a interface de edição do GridView.
Observação
O estado de exibição do 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 OptimisticConcurrency.aspx
página 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 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 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 novo nome do 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 silenciosamente, pois não havia 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 exibir a imagem em tamanho real)
O motivo pelo qual as alterações do segundo navegador não foram confirmadas foi porque a UPDATE
cláusula da WHERE
instrução filtrava todos os registros e, portanto, não afetava nenhuma linha. Vamos examinar a instrução UPDATE
novamente:
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 (já que ele 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 um e salvando 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 à DELETE
cláusula da WHERE
instrução , a exclusão falha silenciosamente.
Da perspectiva 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á comentários visuais de que suas alterações não se mantiveram. O ideal é que, se as alterações de um usuário forem perdidas em uma violação de simultaneidade, notificaremos e, talvez, manteremos a grade no modo de edição. Vamos ver como fazer isso.
Etapa 3: Determinar quando ocorreu uma violação de simultaneidade
Como uma violação de simultaneidade rejeita as alterações feitas, seria bom alertar o usuário quando ocorreu uma violação de simultaneidade. Para alertar o usuário, vamos adicionar um controle Web Rótulo à parte superior da página chamada ConcurrencyViolationMessage
cuja Text
propriedade exibe a seguinte mensagem: Você tentou atualizar ou excluir um registro que foi atualizado simultaneamente por outro usuário. Examine as alterações do outro usuário e, em seguida, refaça sua atualização ou exclusão. Defina a propriedade do CssClass
controle Label como Warning, que é uma classe CSS definida em Styles.css
que exibe o texto em uma fonte vermelha, itálica, em negrito e grande. Por fim, defina as propriedades e EnableViewState
rótulos Visible
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 exibir a imagem em tamanho real)
Ao executar uma atualização ou exclusão, os manipuladores de RowUpdated
eventos e RowDeleted
GridView são acionados depois que seu controle de fonte de dados executa a atualização ou exclusão solicitada. Podemos determinar quantas linhas foram afetadas pela operação desses manipuladores de eventos. Se zero linhas foram afetadas, queremos exibir o ConcurrencyViolationMessage
Rótulo.
Crie um manipulador de eventos para os RowUpdated
eventos e RowDeleted
e adicione o seguinte código:
protected void Products_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
if (e.AffectedRows == 0)
{
ConcurrencyViolationMessage.Visible = true;
e.KeepInEditMode = true;
// Rebind the data to the GridView to show the latest changes
Products.DataBind();
}
}
protected void Products_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
if (e.AffectedRows == 0)
ConcurrencyViolationMessage.Visible = true;
}
Em ambos os manipuladores de eventos, marcar a e.AffectedRows
propriedade e, se for igual a 0, defina a ConcurrencyViolationMessage
propriedade Label s Visible
como true
. No manipulador de RowUpdated
eventos, também instruimos o GridView a permanecer no modo de edição definindo a KeepInEditMode
propriedade como true. Ao fazer isso, precisamos reassociar os dados à grade para que os dados do outro usuário sejam carregados na interface de edição. Isso é feito chamando o método GridView.DataBind()
Como mostra a Figura 9, com esses dois manipuladores de eventos, uma mensagem muito perceptível é exibida sempre que ocorre uma violação de simultaneidade.
Figura 9: Uma mensagem é exibida na face de uma violação de simultaneidade (clique para exibir 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 as opções de controle de simultaneidade. Por padrão, os controles da Web de dados ASP.NET e controles de 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 manipula a maior parte do trabalho para adicionar cláusulas aumentadas WHERE
às instruções e DELETE
geradas automaticamenteUPDATE
, mas há algumas sutilezas na manipulação NULL
de colunas de valor, conforme discutido na seção Manipulando NULL
valores corretamente.
Este tutorial conclui nosso exame do SqlDataSource. Nossos tutoriais restantes voltarão a trabalhar com dados usando o ObjectDataSource e a arquitetura em camadas.
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.