DUVIDA TRY CATCH

LUIS.HERRERA 08/04/2013 12:34:34
#421653
Boa tarde.
Me surgiu uma dúvida com relação a execução do tratamento de erro. Estou criando o sistema em dois idiomas, então tenho um resource para cada idioma (português e inglês). Com isso ao gerar a instrução de erro, chamo um método que identifica o idioma do windows e procura a mensagem correta no resource. Isso está ok.
A dúvida é quando a mensagem é realmente executada, veja:
Clico no botão Incluir no form de cadastro. Mais ou menos assim:
public void BtSalvarRegistro()
{
try
{
EmpresasModelo empresa = new EmpresasModelo();
EmpresasBLL obj = new EmpresasBLL();
empresa.RAZAOSOCIAL = txtRazaoSocial.Text;
empresa.CNPJ = mskCNPJ.Text;
}
catch (Exception ex)
{
if (ex.Message == [Ô]Esta Empresa já está cadastrada[Ô])
{
MessageBox.Show(Idioma.RetornaMensagem([Ô]RegistroJaCadastrado[Ô]), Idioma.RetornaMensagem([Ô]TituloAvisoMSG[Ô]), MessageBoxButtons.OK, MessageBoxIcon.Error);
txtRazaoSocial.Focus();
}
else
{ MessageBox.Show(ex.Message, Idioma.RetornaMensagem([Ô]TituloAvisoMSG[Ô]), MessageBoxButtons.OK, MessageBoxIcon.Error); }
}
}

Na classe BLL está:
public String Incluir(EmpresasModelo empresa)
{
// o nome da empresa é obrigatório
if (empresa.RAZAOSOCIAL.Trim().Length == 0)
{
throw new Exception([Ô]A Razão Social é obrigatória[Ô]);
}
// nome deve ter no mínimo cinco letras
if (empresa.RAZAOSOCIAL.Trim().Length < 5)
{
throw new Exception([Ô]A Razão Social deve ter no mínimo 05 letras[Ô]);
}
//Se chegou aqui é porque está tudo ok, então chama rotina de inserção
EmpresasDAL obj = new EmpresasDAL();
return obj.Incluir(empresa).ToString();
}

Na classe DAL
public Int32 Incluir(EmpresasModelo empresa)
{
// variável para receber ID de registro já cadastrado
Int32 idEmpresaExiste = 0;
bool myResultado = false;

try
{
//primeiro testa se registro já existe para não cadastrar novamente
idEmpresaExiste = JaExiste(empresa.RAZAOSOCIAL.ToLower());

if (idEmpresaExiste == 0)
{
// ações aqui
}
else
{
throw new Exception([Ô]Esta Empresa já está cadastrada[Ô]);
}
}
catch (SqlException ex)
{
throw new Exception([Ô]Servidor SQL Erro: [Ô] + ex.Number);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
Dados.FecharConexao();
}
}

Como dá para ver acima, tem um método chamando outro e assim sucessivamente, então a dúvida é quando eu gero internamento um throw, ele é enviado para quem chamou o método ou será executado no Catch dentro do método que o gerou?

Como seria um procedimento correto, pois estou me perdendo com isso. Eu gero um throw, então dentro desse mesmo método eu tenho de tratar, ou tenho de devolver a quem chamou. Se tiver vários métodos aninhados, quem realmente fica responsável por dar a mensagem ao usuário?

Uma última dúvida é quando as mensagem do sistema, seja do windows ou do SQL server, como estou usando um resource para cada idioma, estou criando as mensagens porém só do que meu sistema usa, e não o que o Windows e o SQL instalado tem. Com isso se a mensagem vier num idioma difernte do usuário, ex o windows é português, mas o SQL está em inglês ou vice-versa, não tem como tratar isso certo? Assim as mensagens ficariam em idioma diferente.
ALEVALE 08/04/2013 13:07:24
#421654
Não é mais fácil você pegar a linguagem cultural se não me engano (culture name).
Assim você verifica pelo sistema operacional.
http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.name(v=vs.71).aspx
LUIS.HERRERA 08/04/2013 13:36:57
#421657
Eu já faço isso, o método Idioma usa a cultura para pegar minhas mensagens do resouce correspondente. As duas dúvidas não são sobre isso, mas:
Se a estrutura de Try Catch e uso do throw estão corretas, para invocar a mensagem correta no lugar certo e se mesmo usando a culture, as informações vindas do SQL siriam relamente exibidas em outro idioma, quando o Windows estiver com cultura diferente.
OCELOT 08/04/2013 13:55:33
#421658
Você tem alguns erros bem básicos ai, primeiro de tudo, só pegue as exceptions se você realmente vai tratar ela. Você só vai relançar uma exception se precisar executar algum código antes de fazer isso.

Vamos a sua dúvida, no seguinte caso

public void Teste()
{
try
{
throw new Exception([Ô]Ocorreu um erro![Ô]);
}
catch (Exception ex)
{

}
}


Neste caso o throw vai ser tratado nesta mesma função pois ele ocorre dentro de um try, não importa de onde veio a exception, se ela ocorreu dentro de um bloco try ela sempre é pega pelo primeiro catch que seja do tipo dela, então no seu exemplo o throw dentro da classe DAL vai ser pego primeiro no próprio catch dela, onde então você está lançando uma nova exception

Porém não é bom fazer isso pois você perde as informações da exception original, neste caso o melhor seria você simplesmente não tratar ela e deixar que ela seja lançada automaticamente para o próximo bloco try/catch que estiver na pilha de chamada de funções

Mas então digamos que eu precise tratar a exception e quer então lançar ela de novo para o próximo lugar tratar ela, o ideal nesse caso é usar apenas throw;
Segue exemplo:

public void Teste()
{
try
{
throw new Exception([Ô]Ocorreu um erro![Ô]);
}
catch (Exception ex)
{
LimpaObjeto();
throw;
}
}


Quando você usa apenas [Ô]throw;[Ô] dentro do catch em vez de [Ô]trow ex;[Ô] ou [Ô]throw new Exception();[Ô] ele mantem todas as informações originais, inclusive em qual linha ocorreu o erro caso esteja em modo debug.

Se você usar [Ô]throw ex;[Ô] ali na hora que você tratar o erro ele vai informar como se o erro tivesse ocorrido ali naquela linha, enquanto que com o [Ô]throw;[Ô] ele ainda informa que a linha é a original que lançou a exception na primeira vez.

Outro detalhe é que você trata as exceptions usando a mensagem para saber qual é qual, o ideal é você criar as suas próprias exceptions, por exemplo em vez de fazer assim

throw new Exception([Ô]Esta Empresa já está cadastrada[Ô]);


eu poderia ter a seguinte classe

public class EmpresaExistenteException : Exception
{
}


e então simplesmente chamar assim:

throw new EmpresaExistenteException();


Como só quero dizer que a empresa já existe não preciso nem mesmo mandar uma mensagem, por isso a classe fica bem simples, não precisa nem de um construtor, posso deixar só no padrão, e etão no BtSalvarRegistro em vez de fazer um if pela mensagem eu colocaria um [Ô]catch (EmpresaExistenteException ex)[Ô], e no seu DAL eu tiraria todos os catch e deixaria só o try/finally, pois os erros lançados ali estão de qualquer jeito chegando até o BtSalvarRegistro então não são realmente necessários.
KERPLUNK 08/04/2013 15:06:11
#421661
Resposta escolhida
O melhor mesmo, é lançar exceptions personalizadas de acordo com o erro, assim, no evento catch de cada bloco try, você intercepta as exceções corretas. Fiz um artigo sobre isso um tempinho atrás.
LUIS.HERRERA 09/04/2013 12:24:05
#421681
Kerplunk boa tarde. Havia esquecido desse PDF, que li antes de efetivamente começar a programas no Net.

Fui tentar fazer isso:
[Serializable]
public class MinhaPropriaException : Exception
{
public MinhaPropriaException() { }
public MinhaPropriaException(string message) : base(message) { }
public MinhaPropriaException(string message, Exception inner) : base(message, inner)
{ }
protected MinhaPropriaException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
As exceptions


Mas após colar esse código no projeto o VS 2008 foi fechado automaticamente.

Onde realmente eu devo colocar isso para ser usado no sistema?

Apos ler tudo, fiquei com uma dúvida:
Au aciono o método Salvar (form) ele chama o método gravar do BLL (sem tratamento de erro) que por sua vez chama o método gravar da DAL e esta ainda chama o método ABRIR CONEXAO.

Bem no ABRIR CONEXAO fiz o tratamento de erro retornando só throw, já no DAL coloquei assim:

.....
Try
{
AbrirConexao();
//declaração de DataTable, etc....
}
Catch (SQLException ex)
{
throw;
}
Catch (Exception ex)
{
throw;
}
Finaly
{
FecharConexao();
}

No método gravar do form eu fiz o tratamento de erro e inclui a MessageBox ao usuário para cada situação. Fiz isso porque dentro das classes BLL e DAL não tem nada visual e não dá para exibir mensagem, assim joguei o tratamento para parte mais externa da chamada (ação do form). Seria isso mesmo?

O problema é que queria fazer a parte personalizada para ao identiricar que um registro já existe no banco, dar a mensagem ao usuário, então pensei no Catch do form personalizado, como sugeriu no PDF, mas deu o erro que citei no início. Como fazer isso?
KERPLUNK 09/04/2013 18:03:31
#421702
Faça a sua exceção personalizada, ser lançada de onde ela pode ser detectada:
Pseudo-código:

MeuMetodo()
{
datareader(select count(0) from clientes where codigo = 1)
if (dr.hasrows)
throw new Namespace.ClienteJaExisteException();
}


Então, em todos os lugares onde se chama o método em que essa exceção é lançada, você pode interceptá-la:

<DAL>
try
{
DAL.MeuMetodo()
}
catch (ClienteJaExisteException)
{
throw;
}


E finalmente na sua tela você vai poder pegar a exceção, o lance, é ficar jogando ela entre todas as camadas.
Tópico encerrado , respostas não são mais permitidas