Como mencionamos anteriormente, os controles vinculados a dados armazenam os valores passados para a fonte de dados em dicionários independentes de Chaves, Valores (novos valores) e OldValues. Por padrão, SqlDataSource e ObjectDataSource ignoram o campo OldValues e usam apenas Chaves e Valores. Esse comportamento é detectado pela propriedade ConflictDetection da fonte de dados, que é definida como OverwriteChanges por padrão. O padrão OverwriteChanges significa "corresponder apenas ao valor da chave primária para atualizar ou excluir o registro". Esta operação significa que a atualização ou exclusão de um registro não leva em consideração se o valor subjacente do registro foi alterado. Em circunstâncias normais, o estado ideal é permitir que a operação Atualizar ou Excluir seja bem-sucedida somente quando o valor da linha de dados corresponder exatamente ao valor selecionado originalmente. Nessa situação ideal, se outro usuário atualizar uma linha entre o momento em que você a seleciona e o momento em que você a atualiza, sua operação de atualização falhará. A fonte de dados também dá suporte a essa operação configurando a propriedade ConflictDetection como CompareAllValues. Neste modo, a fonte de dados aplicará OldValues ao comando ou método e usará esses valores para garantir que a operação de atualização ou exclusão corresponda a todos os valores do registro antes de atualizar ou excluir o registro. Você também deve definir a propriedade OldValuesParameterFormatString como uma string de formato de componente válida do .NET Framework (como "original_{0}") para indicar como os parâmetros nos dicionários OldValues e Keys devem ser renomeados para distingui-los dos parâmetros NewValues.
O exemplo de código a seguir mostra comandos SQL típicos usados pelo controle SqlDataSource nos modos OverwriteChanges e CompareAllValues. O campo ID é considerado o campo de chave primária. Observe que o último comando compara todos os valores brutos das linhas de dados na cláusula WHERE, em vez de apenas as chaves primárias. Nesse caso, OldValuesParameterFormatString da fonte de dados precisa ser definido como "original_{0}".
SELECIONE [ID], [Nome], [Endereço] em [Contatos]
-- Substituir alterações
ATUALIZAR [Contatos] SET [Nome] = @Nome, [Endereço] = @Endereço WHERE [ID] = @ID
DELETE FROM [Contatos] WHERE [ID] = @ID
-- CompareAllValues
ATUALIZAR [Contatos] SET [Nome] = @Nome, [Endereço] = @Endereço WHERE [ID] = @original_ID
AND [Nome] = @nome_original E [Endereço] = @Endereço_original
DELETE FROM [Contatos] ONDE [ID] = @original_ID AND [Nome] = @original_Name
AND [Address] = @original_Address
Observe que OldValues não são necessários para operações de inserção e ConflictDetection só é significativo para operações de atualização e exclusão.
O exemplo a seguir demonstra o comportamento quando ocorre um conflito. Para executar este exemplo, você deve abrir duas instâncias do exemplo em duas janelas separadas do navegador (clique duas vezes em "Executar amostra"). Em seguida, clique no botão "Editar" na mesma linha de ambos os formulários para colocar a linha no modo de edição. Altere um valor na primeira janela e clique em "Atualizar". Na segunda janela, insira um novo valor na linha e clique em “Atualizar”. Esta operação de atualização não foi bem-sucedida porque o valor da linha de dados subjacente já foi alterado pela primeira operação de atualização. Este exemplo verifica a propriedade AffectedRows do parâmetro de evento Updated ou Deleted, que é 0 para confirmar que ocorreu um conflito.
<script runat="servidor">
Sub protegido SqlDataSource1_Updated(remetente como objeto, e como SqlDataSourceStatusEventArgs)
Se e.AffectedRows = 0 Então
Response.Write("Linha alterada, atualização abortada<br />")
Terminar se
End Sub
Protected Sub SqlDataSource1_Deleted(sender As Object, e As SqlDataSourceStatusEventArgs)
Se e.AffectedRows = 0 Então
Response.Write("Linha alterada, exclusão abortada<br />")
Terminar se
Finalizar sub
</script>
Quando Update ou Delete usa a UI modelada, os valores antigos dos campos de vinculação de dados bidirecionais usando a sintaxe Bind serão mantidos. Para Excluir, isso significa que você deve usar a sintaxe Bind para valores vinculados a dados no ItemTemplate. O objetivo é reter o valor antigo necessário para a operação de exclusão. O exemplo a seguir demonstra essa técnica.
<asp:GridView......>
<Colunas>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:TemplateField HeaderText="ContactID" InsertVisible="False" SortExpression="ContactID">
<ItemTemplate>
<asp:Label ID="Label1" runat="servidor" Text='<%# Bind("ContactID") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:Label ID="Label3" runat="servidor" Text='<%# Eval("ContactID") %>'></asp:Label>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="NomeContato" SortExpression="NomeContato">
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("ContactName") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="servidor" Text='<%# Bind("NomeContato") %>'></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
</Colunas>
</asp:GridView>
Você pode lidar com erros de detecção de conflitos suavemente, avisando ao usuário que os dados subjacentes foram alterados, exibindo o valor alterado para o usuário e permitindo que o usuário opte por enviar ou abandonar sua operação. O exemplo a seguir demonstra uma maneira possível de lidar com a detecção de conflitos. Observe que o parâmetro de evento RowUpdated do DetailsView recebe um dicionário que pode ser usado para detectar valores inseridos pelo usuário. Você também pode definir a propriedade KeepInEditMode deste parâmetro de evento para manter o DetailsView no modo de edição enquanto o usuário decide como lidar com o conflito. Este exemplo testa uma abordagem semelhante à anterior, abrindo duas janelas simultaneamente para criar atualizações conflitantes.
SubDetalhes ProtegidosView1_ItemUpdated(ByVal remetente As Object, ByVal e As System.Web.UI.WebControls.DetailsViewUpdatedEventArgs)
Se e.AffectedRows = 0 Então
' Coloca o DetailsView em modo de edição e sincroniza com o banco de dados e.KeepInEditMode = True
DetalhesView1.DataBind()
'Preenche novamente o DetailsView com valores inseridos pelo usuário
Dim t como TextBox
t = DetalhesView1.Rows(1).Células(1).Controles(0)
t.Text = e.NewValues("DataDoPedido")
t = DetalhesView1.Rows(2).Células(1).Controles(0)
t.Text = e.NewValues("ShipCountry")
ErrorPanel.Visible = True
Outro
ErrorPanel.Visible = Falso
Terminar se
End Sub
Protected Sub DetailsView1_ModeChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewModeEventArgs)
Se e.CancelingEdit = True AndAlso ErrorPanel.Visible = True Então
ErrorPanel.Visible = Falso
Terminar se
A situação é semelhante quando
End Sub
usa ObjectDataSource.Observe que, como a propriedade ConflictDetection da fonte de dados está definida como CompareAllValues, a fonte de dados procurará uma sobrecarga UpdateContact que aceite o valor bruto de cada campo do objeto Contact.
Você também pode usar a propriedade DataObjectTypeName e CompareAllValues juntas. Nesse caso, o ObjectDataSource procura uma sobrecarga UpdateContact que aceita apenas dois parâmetros (ambos Contact). O primeiro parâmetro é o objeto Contact que armazena o novo valor e o segundo parâmetro é o objeto Contact que armazena o valor antigo.