DESVIO CATCH

LUIS.HERRERA 19/08/2014 10:14:29
#440582
Bom dia.
Preciso saber duas coisas:
1- Como sair de um Try Catch e retornar ao código (tentar novamente)
2- Qual o número do erro gerado pelo SQL Server 2008 quando se tenta incluir uma chave primária repetida?

Minha Tabela Ficou assim
IDRegistro = int, identify
(IDEmpresa e IDSequencia ) = int e chave componta para só permitir uma combinação igual 1,1 - 1,2, etc...

Num DAL específico estou fazendo assim:

  
int novoID = 0;
...
try
{
...
AbreBanco()
string sql = [Ô]INSERT INTO Tabela (IDEMPRESA, IDSEQUENCIA, DESCRICAO) VALUES (@IDEMPRESA, ISNULL((SELECT Max(IDSEQUENCIA) FROM Tabela WHERE IDEMPRESA = @IDEMPRESA),0) + 1, @DESCRICAO); [Ô]
+ [Ô]SELECT CAST(scope_identity() AS int)[Ô];

SqlCommand cmd = new SqlCommand(sql, Dados.cn);
cmd.Parameters.Add([Ô]@IDEMPRESA[Ô], SqlDbType.int);
cmd.Parameters[[Ô]@IDEMPRESA[Ô]].Value = myIDEMPRESA;
cmd.Parameters.Add([Ô]@DESCRICAO[Ô], SqlDbType.NVarChar);
cmd.Parameters[[Ô]@DESCRICAO[Ô]].Value = myDescricao;
[txt-color=#e80000]RepetirInclusao: [/txt-color] //Aqui é um rótulo para voltar e tentar novamente se der erro na inclusão, caso haja concorrência por dois usuários IDSequencia igual para mesma empresa, ou seja (chave composta violada por cadastrar numero repetido).
novoID = Convert.ToInt32(cmd.ExecuteScalar());
}
catch(SQLException ex)
{
if (ex.Number == xxx) [txt-color=#e80000]// como saber o erro para essa violação da chave[/txt-color]
{
goto RepetirInclusao; [txt-color=#e80000]//Aqui dá erro, pois diz que o rótulo não faz parte do escopo. Não está dentro do catch. Como resolver isso
[/txt-color] }
else
{
throw;
}
}
catch (Exception)
{ throw; }
finally
{
FecharConexao();
}
return novoID; //se devolver zero ocorreu erro


Como resolver isso?
SEPULTURA 19/08/2014 11:03:29
#440586
Qual seu banco de dados ?

LUIS.HERRERA 19/08/2014 11:03:37
#440587
SQL SERVER 2008 R2 Express

Achei uma solução que aparentemente não deu problema.
Será que isso irá funcionar?
Como posso descobrir o número do erro citado no Catch para violação chave primária/composta?

Solução:
  
int novoID = 0;
bool DeuErroNaInclusao = false; //Usado para identificar se deu erro e repetir o código de inserir
...
RepetirInclusao: //Aqui é um rótulo para voltar e tentar novamente se der erro na inclusão, caso haja concorrência por dois usuários IDSequencia igual para mesma empresa, ou seja (chave composta violada por cadastrar numero repetido).

try
{
...
AbreBanco()
string sql = [Ô]INSERT INTO Tabela (IDEMPRESA, IDSEQUENCIA, DESCRICAO) VALUES (@IDEMPRESA, ISNULL((SELECT Max(IDSEQUENCIA) FROM Tabela WHERE IDEMPRESA = @IDEMPRESA),0) + 1, @DESCRICAO); [Ô]
+ [Ô]SELECT CAST(scope_identity() AS int)[Ô];

SqlCommand cmd = new SqlCommand(sql, Dados.cn);
cmd.Parameters.Add([Ô]@IDEMPRESA[Ô], SqlDbType.int);
cmd.Parameters[[Ô]@IDEMPRESA[Ô]].Value = myIDEMPRESA;
cmd.Parameters.Add([Ô]@DESCRICAO[Ô], SqlDbType.NVarChar);
cmd.Parameters[[Ô]@DESCRICAO[Ô]].Value = myDescricao;
novoID = Convert.ToInt32(cmd.ExecuteScalar());
}
Catch(SQLException ex)
{
if (ex.Number == xxx) [txt-color=#e80000]// Só Falta saber o erro para essa violação da chave[/txt-color]
{
DeuErroNaInclusao = true;
}
else
{
throw;
}
}
Catch (Exception)
{ throw; }
finally
{
FecharConexao();
}
if (DeuErroNaInclusao == true)
{
goto RepetirInclusao;
}
else
{
return novoID; //se devolver zero ocorreu outro tipo de erro.
}

SEPULTURA 19/08/2014 11:14:42
#440588
Voce cria a tabela com autonumerador automatico com identity não prefere assim

CREATE TABLE Tabela ( IDSEQUENCIA INT IDENTITY(1,1) NOT NULL ,
IDEMPRESA INT NOT NULL ,
DESCRICAO VARCHAR (80))

LUIS.HERRERA 19/08/2014 11:25:18
#440589
Sepultura, vou tentar melhorar a explicar anterior.
A tabela já tem um campo identify: IDRegistro = int, identify

O campo IDSequencia é iniciado ou reiniciado para cada nova empresa usada no sistema, então não pode ser identify, já que esse campo terá numeração repetida para cada empresa, por isso tenho uma chave composta para esses dois campos (IDEmpresa e IDSequencia). Com isso os registros serão gravados assim:

IDRegistro (identify) - IDEmpresa - IDSequencia - Descricao
1 1 1 jjfdkjfkfjkdj
2 1 2 uiukjkrjkrjaejr ek
3 2 1 uireurejkrjekrjek
4 1 3 euri8437iuiquriuri
5 3 1 urrrr
etc......

Depois de pesquisar muito, acho que não há alternativa nesse caso, sendo obrigado a usar o [Ô]Terrível[Ô] Max + 1, uma vez que a numeração sequencial deve iniciar para cada empresa diferente, não pode ter lacunas (furos) e não pode ser repetida na mesma empresa, por isso a chave composta (IDEmpresa e IDSequencial). A coisa ainda é mais complexa que isso, pois envolvem outras tabelas auxiliares, porém se eu resolver isso com SEGURANÇA, vou contornar as outras tabelas de outra forma, e evitar essa mesma situação, porém para essa tabela principal não tem outra forma, tem que ser assim o cadastro.
SEPULTURA 19/08/2014 11:34:36
#440590
Assim eu tb , já tentei mudar o max + 1 mais , ainda nao consegui , mais ele não da problema mais , é por que estou arrumar isso no sql , então nao precisa de Identity

mais por que usar IDSEQUENCE , é só voce inibir o campos códigos , e sua base já servira para Web como pra Desktop ... todos as validacoes voce pode fazer

EMPRESA + FORN + DOC


Atenciosamente ,
Abraços
SEPULTURA 19/08/2014 11:38:32
#440592
Espero te ajudado , do try catch finally .

Try
sr = [ô][ô]
sr = rf
catch to objetoDeErro && Cria o objeto com os dados do Erro
slog = [ô]Erro: [ô]+(objetoDeErro.message) && Propriedade message do objeto de Erro.
Messagebox(slog) && Exibe o erro.
finally && Executa depois da mensagem.
quit
clear events
endtry
FFCOUTO 19/08/2014 11:53:02
#440593
Luis, acho que usando apenas um do.. while resolve seu problema.

bool duplicateKeysFound = false;

do
{
try
{
// seu código aqui
}
catch (SqlException ex)
{
if (ex.Number == xxx) // erro de violação da chave
{
duplicateKeysFound = true;
}
else
{
throw;
}
}
catch (Exception ex)
{
throw;
}
}
while (duplicateKeysFound);


Não testei, mas é o caminho lógico para essa repetição sem usar o GoTo.
LUIS.HERRERA 19/08/2014 15:13:56
#440595
Grande Fabiano, blz?
Realmente usar o do... while é muito melhor que um if com o goto, sem comentários. Por falar nisso só pensei no goto (nunca mais quero usar isso), pois não sabia como sair do Catch, mas os problemas são ótimos, pois nos forçam a testar, pesquisar e compartilhar conhecimentos com os amigos.

Quero agradecer a todos que colaboraram, pois consegui resolver o problema. Ajustei o código que postei com a dica do Fabiano Couto, e descobri o código de erro de violação da chave, para quem quiser saber é 2627.

Fiz testes de concorrência e funciona perfeitamente, apesar da rotina não ser tão crítica assim, e a possibilidade de concorrência é bem pequena. Acho que não haverá nenhum problema, se houver, a rotina irá resolver, pois ficará em loop até conseguir gravar um número válido o que deve ocorrer no máximo na terceira tentativa se chegar a tanto.
Tópico encerrado , respostas não são mais permitidas