none
Tratamento de erro nos modelos com EF - Melhores práticas e Log RRS feed

  • Pergunta

  • Olá.

    Estou indo muito bem com ASP.Net MVC e pelo que estou vendo até agora está dando um banho no Zend Framework do PHP (que programei nos últimos 2 anos).

    Agora veio uma dúvida quanto ao tratamento de erros.

    Por exemplo na hora de excluir um registro do banco de dados e ter uma FK envolvida.

    Daí veio a dúvida: O tratamento do erro com Try\Catch e lançamento de minhas próprias exceções, eu faço no Repository ou na controller?  Claro que talvez possa ser "o que você preferir" mas gostaria de saber dos mais experientes.

    Um exemplo simples do meu código.

    Código do Repositório:

    public class ColaboradorRepository: RepositoryBase
        {
            public void Excluir(int pID)
            {
                Colaborador C = _Contexto.DbColaborador.Find(pID);
                _Contexto.DbColaborador.Remove(C);
                _Contexto.SaveChanges();
            }
    }

    Código da Controladora:

    [HttpPost]
            public ActionResult Excluir(ColaboradorExcluirView pDados)
            {
                ColaboradorRepository CR = new ColaboradorRepository();
                CR.Excluir(pDados.Codigo);
                CR.Dispose();
    
                return RedirectToAction("Listar");
            }

    Daí eu coloco um Try\Catch dentro do repositório ou na controler?

    Eu acho que no repositório pois por exemplo o Excluir do ColaboradorRepository eu poderia fazer outras operações, inclusive com outros modelos.

    E quanto a gravação de logs de erro, ví o tal do log4net.  Vocês utilizam ele?

    quinta-feira, 7 de março de 2013 20:28

Todas as Respostas

  • Bom, o ideal mesmo seria fazer nos dois locais.

    No repositório você pode recuperar uma exceção mais específica relativa ao banco de dados e retornar uma mensagem correspondente em português por exemplo.

    No controller você pode gerenciar como a aplicação irá tratar tal exceção, se lança a mesma como veio ou trata e exibe uma mensagem de erro amigável após o catch.

    Quanto ao log as melhores opções são o log4net mesmo ou o elmah.

    quinta-feira, 7 de março de 2013 21:40
    Moderador
  • Sim, pensando ontem a noite no Repository vou fazer o Try\Catch e tratar todas as exceções que possam ocorrer.   Vou criar um método que envia a Exception e lá ela é analisada e um texto comum será gerado informando o ocorrido.  Neste momento o log será gravado também.

    Daí no Repositorio eu lanço outra exception, mas uma minha com texto pleno do ocorrido e daí na controladora eu pego isso com Catch e adiciono o texto puro no modelstate.adderror.

    Vlw obrigado.

    sexta-feira, 8 de março de 2013 11:37
  • Se fosse fizer todo o tratamento dentro do repositorio você NÃO consiguirá capturar excessoes especificas no controller, portanto eu faço no controller.

    Porêm dependendo da ação executada no repositorio eu tambem posso ter um try/catch dentro do repositorio.

    Enfim, se você quiser capturar erros especificos, o ideal é fazer no controller, caso contrário como você vai saber qual excessão foi gerada?


    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    sexta-feira, 8 de março de 2013 11:56
  • Por que não conseguiria?

    Controler:

    try

      Repository.GravarDados()
    catch (MinhaException ex)

    {

    }

    No Repository
    public void GravarDados()
    {

       try

         Contexto.SaveChanges()

      catch (..... ex)
        TratarExceptionContexto(ex)

     

    }

    public void TratarExceptionContexto(exception ex)
       tratamento qualquer

       throw new MinhaException("erro")

    Dá certo sim.

    sexta-feira, 8 de março de 2013 12:28
  • E se você tiver 2 ou mais excessões diferentes se tratar tudo no repositorio e somente um retornar um 'MinhaException' com uma mensagem?

    Como vai saber qual é qual?

    Exemplo no controller:

    public ActionResult Index(){
         try{
    
         }
         catch(MinhaException ex){
              //aqui eu so tenho a mensagem.. não sei oq realmente aconteceu
              // como fazer caso exista várias respostas diferentes?
         }
    }


    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    sexta-feira, 8 de março de 2013 14:28
  • Assim:

    Método SalvarDados do Repository:

    public void SalvarDados()
    {
    	try
    	{
    		_Contexto.SaveChanges();
    	}
    	catch (DbEntityValidationException ex)
    	{
    		throw new Exceptions.GestorWebException(ex);
    	}            
    	catch (DbUpdateConcurrencyException ex)
    	{
    		throw new Exceptions.GestorWebException(ex);
    	}
    	catch (DbUpdateException ex)
    	{
    		throw new Exceptions.GestorWebException(ex);
    	}
    	catch (Exception ex)
    	{
    		throw new Exceptions.GestorWebException(ex);
    	}
    }

    A exception é capturada e uma única classe trata elas.

    E minha classe GestorWebException, ainda em implementação:

    public class GestorWebException: Exception
    {
    	public List<string> MensagensRetorno = new List<string>();
    	public GestorWebException(Exception pEx)
    	{
    		MensagensRetorno.Add("Exception");
    	}
    	public GestorWebException(DbUpdateException pEx)
    	{
    		MensagensRetorno.Add("DbUpdateException");
    	}
    	public GestorWebException(DbUpdateConcurrencyException pEx)
    	{
    		MensagensRetorno.Add("DbUpdateConcurrencyException");
    	}
    	public GestorWebException(DbEntityValidationException pEx)
    	{            
    		MensagensRetorno.Add("DbEntityValidationException");
    	}
    }

    Daí nesta classe acima é que eu faço o tratamento, vejo códigos de rros e monto a mensagem corretamente.

    E na controladora:

    [HttpPost]
    public ActionResult Excluir(ColaboradorExcluirView pDados)
    {
    	try
    	{
    		using (ColaboradorRepository CR = new ColaboradorRepository())
    		{
    			CR.Excluir(pDados.Codigo);
    		}                
    	}
    	catch (Exceptions.GestorWebException pEx)
    	{
    		foreach (string Mensagem in pEx.MensagensRetorno)
    			ModelState.AddModelError("", Mensagem);
    		return View(pDados);
    	}
    	return RedirectToAction("Listar");
    }

    Daí pensei desta forma.  Testei e funcionou muito bem.

    Agora é estudar as exceptions do EF para saber exatamente o que está ocorrendo.

    Alguém aí tem algum código que faz log das propriedades mais importantes das exceptions do EF?

    Falou

    sexta-feira, 8 de março de 2013 17:05
  • Certo, mas você ainda não consegue com facilidade saber qual excessão aconteceu!!

    Tudoq eu acontece no seu sistema é tratado com uma única excessão?

    E se você tiver uma situação especifica?

    Por exemplo, este é um código que uso em um projeto meu:

            public JsonResult AddManagerPoint(ManagerVoteViewModel managerVote)
            {
                if (ModelState.IsValid)
                {
                    try
                    {
                        _userVoteService.SaveManagerVote(managerVote);
                    }
                    catch (NotRelatedUser ex)
                    {
                        //faz alguma coisa
                    }
                    catch (NotEnoughVotes ex)
                    {
                        //faz outra coisa
                    }
                    catch (DbEntityValidationException ex)
                    {
                        //faz outra coisa
                    }
                    catch (Exception)
                    {
                        //faz outra coisa 
                    }
    
                    return Json(true);
                }
    Entendeu? você não consegue com failidade resolver excessoes especificas.


    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    sexta-feira, 8 de março de 2013 17:42
  • Sim concordo com você Murilo.

    Eu fiz da forma que mostrei para a maioria dos casos.  Geralmente qualquer coisa que impeça de gravar um registro (NotEnoughVotes) por exemplo eu irei verificar sem lançar exception.    Trato exception para erros do banco mesmo, ou falhas diversas.  Daí centralizo tudo.  

    Na classe de tratamento se ver que é um erro mais grave, ali mesmo já chama algo pra enviar e-mail e direcionar para uma pagina de erro.

    Pois terei dezenas de controllers e actions e quero deixar o try\catch o mais padronizado possível.

    sexta-feira, 8 de março de 2013 17:57