none
Falha na atualização de registro. RRS feed

  • Pergunta

  • Boa tarde,

    Estou tendo um problema para fazer uma atualização de um registro utilizando LinqToSQL. Vou tentar explicar o cenário o máximo possível.

    Tenho uma tabela "GUIAS" e "MOVIMENTOGUIA". Após o realizar uma movimentação na guia faço um update do status na tabela GUIAS. Até ai tudo bem, o problema é que as vezes a atualização do status não é efetuada.

    Antes eu utilizava o seguinte código:

    MovimentoGuia mov = ISelecionaMovimentoPorIdMov(idMov);
    mov.IdUsuarioRecebimento = idUsusarioRec;
    mov.DataRecebimento = dtRec;
    
    //atualizando status
    Guia g = SelecionaGuia(idGuia);
    g.idSetor = Setor;
    
    dc.SubmitChanges();
    

    Mas algumas vezes (não acontecia sempre) o sistema gerava o seguinte erro "System.Data.Linq.ChangeConflictException: Row not found or changed".

    Então pesquisei na internet e achei uma solução, conforme abaixo:

    MovimentoGuia mov = ISelecionaMovimentoPorIdMov(idMov);
    mov.IdUsuarioRecebimento = idUsusarioRec;
    mov.DataRecebimento = dtRec;
    
    //atualizando status
    Guia g = SelecionaGuia(idGuia);
    
    var id = dc.Setors.Single(c => c.idSetor == Setor);
    g.Setor = id;
    dc.SubmitChanges();

    Mudando para o código acima, não tive mais o erro. Porém, algumas vezes, o movimento era efetuado mas o update do setor não, o que gerava algumas inconsistências na aplicação.

    Já fiz algumas mudanças no código para tentar resolver o problema, por exemplo:

    Isolei o código da atualização do setor em outro método (sem sucesso);

    utilizei o TransactionScope() envolvendo o código e comitando a transação no final (sem sucesso).

    Por este motivo venho pedir ajuda do fórum.

    Obrigado.

    quarta-feira, 8 de julho de 2015 18:24

Respostas

  • Ideal,

     seria seu contexto ser criado sempre em um bloco using por exemplo :

    private DataDBDataContext dc = null;
    
    //nesse seu metodo
    using(dc = new DataDBDataContext())
    {
    
    }
    //ao sair do bloco ja vai ser retirado da memoria

    • Marcado como Resposta galves.rod segunda-feira, 21 de setembro de 2015 18:31
    quinta-feira, 20 de agosto de 2015 13:39
  • Olá a todos,

     boa tarde, Galves encontrei um problema parecido com o seu hoje utilizando LinqToSql !! Há algum erro no ORM ou bug não descobri o motivo ainda, mas ocorreu ao utilizar "Unit of Work" ou seja uma instância do contexto faz os updates, select, insert etc. Gerava erro no Update ! 

     Solução : A cada operação foi criado uma nova instância do contexto igual postei no exemplo meu acima ! Após fazer isso funcionou normalmente, quando encontrar algo que explique tecnicamente o motivo volto a postar, caso contrario quem utiliza esse ORM faça sempre uma nova instância do contexto para cada operação.

    • Marcado como Resposta galves.rod segunda-feira, 21 de setembro de 2015 18:32
    segunda-feira, 14 de setembro de 2015 18:08

Todas as Respostas

  • Olá Galves,

     geralmente no LinqToSql eu faço a busca do objeto e atualizo por exemplo :

    context.MinhaTabela.Attach(tabela);
                        context.Refresh(RefreshMode.KeepCurrentValues, tabela);
                        context.SubmitChanges();
     Ou busco o registro verifico se o mesmo não é nulo e salvo igual a você. Essa aplicação é web ? Não está havendo atualização concorrente ?

    quarta-feira, 8 de julho de 2015 19:10
  • Boa tarde,

    Obrigado pela resposta. Minha aplicação é WinForm. Já imaginei que o problema fosse este mesmo, pois nunca consigo simular o erro.

    Acho que não entendi o seu exemplo direito. Depois de atualizar os registro que necessito eu colocaria o seu código exemplo?

    Desde já agradeço.

    quarta-feira, 8 de julho de 2015 19:22
  • Não, 

     esse código faz o Update após você buscar sua entidade na fonte de dados.

     Você deve verificar o erro em qual entidade ocorre,  faça assim :

    //1 Update
    var tabelaUm = contexto.TabelaUm.Where(i => i.id == Id)
                           .FirstOrDefault();
    
     if(tabelaUm != null)
         tabelaUm.Nome = "teste";
    
    context.SubmitChanges();
    
    //2 Update
    var tabelaDois = contexto.TabelaDois.Where(i => i.id == Id)
                           .FirstOrDefault();
    
     if(tabelaDois != null)
         tabelaDois.Nome = "teste";
    
    context.SubmitChanges();

     Tente dessa maneira, usei como exemplo os dados de tabela um e tabela dois. No codigo que lhe passei utilizando a variavel tabelaUm ficaria assim :

    ontext.TabelaUm.Attach(tabelaUm);
                        context.Refresh(RefreshMode.KeepCurrentValues, tabelaUm);
                        context.SubmitChanges();

     Faça esses testes, tente verificar em qual dois dois Update ocorre a excecao...

    quarta-feira, 8 de julho de 2015 20:03
  • Bom dia,

    Desculpe, acho que fiz algo errado. Incluí o código do Attach mas ta gerando uma exceção, InvalidOperationException: {"Não é possível anexar uma entidade que já existe."}.

    Segue como ficou o código.

    MovimentoGuia mov = ISelecionaMovimentoPorIdMov(idMov);
    mov.IdUsuarioRecebimento = idUsusarioRec;
    mov.DataRecebimento = dtRec;
    dc.MovimentoGuias.Attach(mov);
    dc.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, mov);
    //dc.SubmitChanges();
    //mov = null;
    
    //atualizando status
    Guia g = SelecionaGuia(idGuia);
    //g.idSetor = Setor; //erros
    var id = dc.Setors.Single(c => c.idSetor == Setor);
    g.Setor = id;
    dc.Guias.Attach(g);
    dc.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, g);
    dc.SubmitChanges();
    Obrigado.

    quinta-feira, 9 de julho de 2015 13:54
  • Olá galves, 

     desculpe a demora...

      Isso vai gerar erro mesmo, pois você está no mesmo contexto para utilizar o Attach você deveria estar em outro contexto ou outra instancia...

     Unico motivo que vejo para seu Update falhar é não encontrar o registro, veja dessa forma essa lógica :

    try
    {
    //1 Update
    var tabelaUm = contexto.TabelaUm.Where(i => i.id == Id)
                           .FirstOrDefault();
    
     if(tabelaUm != null)
         tabelaUm.Nome = "teste";
    else 
     {
       //AQUI SEU REGISTRO NAO FOI ENCONTRADO
       
    }
    
    context.SubmitChanges();
    
    }
    catch(Exception ex)
    {
      trhow; //vai travar sua aplicacao
      //ou faca um log aqui para capturar as excessoes
    }
     Vamos lá, caso caia no "else" seu registro nao foi encontrado e significa que o parametro está errado, se cair na exception eu deixei um trhow que trava a execução do codigo, caso seu aplicativo esteja em produção você pode fazer um log para capturar qual erro está sendo lançado, faça os testes e retorne.

    quarta-feira, 15 de julho de 2015 10:49
  • Obrigado, vou fazer um teste e assim que tiver algo volto a comentar.

    Agradeço a atenção.

    sexta-feira, 17 de julho de 2015 14:58
  • Bom dia,

    Desculpe pela demora no retorno, eu estava um pouco apertado no trabalho e nem tinha conseguido testar a solução.

    Alterei o código hoje e já a coloquei em produção. Vou acompanha por cerca de uma semana e vou a responder com o resultado.

    atte;

    quarta-feira, 22 de julho de 2015 13:32
  • Boa tarde,

    Fiz as alterações no código e deixei em produção por uma semana, e o erro ainda ocorre. Segue como ficou meu (método inteiro) código agora:

            public static bool ReceberGuia(int idMov, int idUsusarioRec, DateTime dtRec, int idGuia, int Setor)
            {
                var erro= true;
                try
                {
                    using (var transaction = new TransactionScope())
                    {
                        MovimentoGuia mov = ISelecionaMovimentoPorIdMov(idMov);
                        mov.IdUsuarioRecebimento = idUsusarioRec;
                        mov.DataRecebimento = dtRec;
    
                        var setorGuiaAtualizar = dc.Guias.Where(i => i.IDGuia == idGuia).FirstOrDefault();
    
                        if (setorGuiaAtualizar != null)
                            setorGuiaAtualizar.idSetor = Setor;
                        else
                            throw new Exception();
    
                        dc.SubmitChanges();
                        erro = false;
                        transaction.Complete();
                    }
                }
                catch (TransactionAbortedException ex)
                {
                    GerarLogTxt(ex);
                    erro = true;
                    System.Windows.Forms.MessageBox.Show(ex.Message);
                    EnviarEmail.EnviarEmail msg = new EnviarEmail.EnviarEmail("smtp.gmail.com",
                              587,
                              @"sisag@hnsf.com.br",
                              "hnsf2039",
                              @"sisag@hnsf.com.br",
                              @"gabriel.ti@hnsf.com.br",
                              "ERRO TRANSACT",
                              ex.ToString());
                }
                catch (Exception ex)
                {
                    GerarLogTxt(ex);
                    erro = true;
                    System.Windows.Forms.MessageBox.Show(ex.Message);
                    EnviarEmail.EnviarEmail msg = new EnviarEmail.EnviarEmail("smtp.gmail.com",
                          587,
                          @"sisag@hnsf.com.br",
                          "hnsf2039",
                          @"sisag@hnsf.com.br",
                          @"gabriel.ti@hnsf.com.br",
                          "ERRO TRANSACT GEAL",
                          ex.ToString());
                }
                return erro;
            }


    O código faz uma chamada ao método que realiza a gravação do log, conforme trecho a seguir:

             public static void GerarLogTxt(Exception ex)
            {
                string nomeDoArquivotxt = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath,
                        "Capturas", @"logErroSetor.txt");
    
                if (!System.IO.File.Exists(nomeDoArquivotxt))
                    System.IO.File.Create(nomeDoArquivotxt).Close();
    
                System.IO.TextWriter arquivo = System.IO.File.AppendText(nomeDoArquivotxt);
                arquivo.WriteLine(DateTime.Now);
                arquivo.WriteLine();
                arquivo.WriteLine(ex.ToString());
                arquivo.WriteLine("=============================================================");
                arquivo.WriteLine();
                arquivo.WriteLine();
                arquivo.Close();
            }

    Nada é gerado no log, logo, pressuponho que o "setorGuiaAtualizar" é sempre diferente de "null" e o "else" não está sendo executado.

    Então, o código não gera erro e o "idSetor" ainda não está sendo atualizado em alguns momentos. Alguma outra ideia de que pode estar acontecendo?

    atte;



    • Editado galves.rod sexta-feira, 31 de julho de 2015 17:07
    terça-feira, 28 de julho de 2015 20:40
  • Olá,

     alguma solução ? A variavel "dc" é instanciada na classe ?

    quarta-feira, 19 de agosto de 2015 12:27
  • Bom dia,

    Apesar de não ser o que eu queria, eu criei uma PROC que verifica quando existe um setor que não foi atualizado e o atualiza. Assim eu resolvi o problema para pelo menos o sistema não ser um incomodo para o usuário.

    Sim, instanciei no começo da classe, conforme código abaixo. Será este o problema?

    namespace DAL
    {
        public class DAL_ControleDeGuais
        {
            private static DataDBDataContext dc = new DataDBDataContext(ConexaoBD.ConectarBanco());
    /*...*/
    }
    }
    Obrigado.
    quinta-feira, 20 de agosto de 2015 13:14
  • Bom dia,

    Apesar de não ser o que eu queria, eu criei uma PROC que verifica quando existe um setor que não foi atualizado e o atualiza. Assim eu resolvi o problema para pelo menos o sistema não ser um incomodo para o usuário.

    Sim, instanciei no começo da classe, conforme código abaixo. Será este o problema?

    namespace DAL
    {
        public class DAL_ControleDeGuais
        {
            private static DataDBDataContext dc = new DataDBDataContext(ConexaoBD.ConectarBanco());
    /*...*/
    }
    }
    Obrigado.
     Parece que estamos caminhando, retire o "static" desse contexto, pode instanciar ele no construtor dessa classe ou deixa ai mesmo mas sem static, isso deve gerar alguns erros caso esteja utilizando esse contexto compartilhado, uma dica evite static em acesso a dados... Eu nunca utilizei o TransactionScope e nunca obtive erro com update, creio que seu problema seja no contexto mesmo.... 
    quinta-feira, 20 de agosto de 2015 13:27
  • Ideal,

     seria seu contexto ser criado sempre em um bloco using por exemplo :

    private DataDBDataContext dc = null;
    
    //nesse seu metodo
    using(dc = new DataDBDataContext())
    {
    
    }
    //ao sair do bloco ja vai ser retirado da memoria

    • Marcado como Resposta galves.rod segunda-feira, 21 de setembro de 2015 18:31
    quinta-feira, 20 de agosto de 2015 13:39
  • Boa tarde,

    Vou fazer a alteração e volto com um posição. Aproveitando, você pode me explicar qual o objetivo de se usar o  "using" e/ou vantagens?

    quinta-feira, 20 de agosto de 2015 15:51
  • Boa tarde,

    Vou fazer a alteração e volto com um posição. Aproveitando, você pode me explicar qual o objetivo de se usar o  "using" e/ou vantagens?

    Claro,

     a vantagem é que apos o fim do bloco using caso seu objeto implemente a interface IDisposable, será executado o método Dispose retirando os itens da memoria automaticamente.. veja um exemplo para melhor entendimento :

    Exemplo interface IDisposable

     O LinqToSql implementa a interface, então ao utilizar dentro do bloco using você não se preocupa em "retirar os recursos da memória" é feito automaticamente.

    sexta-feira, 21 de agosto de 2015 12:27
  • Olá a todos,

     boa tarde, Galves encontrei um problema parecido com o seu hoje utilizando LinqToSql !! Há algum erro no ORM ou bug não descobri o motivo ainda, mas ocorreu ao utilizar "Unit of Work" ou seja uma instância do contexto faz os updates, select, insert etc. Gerava erro no Update ! 

     Solução : A cada operação foi criado uma nova instância do contexto igual postei no exemplo meu acima ! Após fazer isso funcionou normalmente, quando encontrar algo que explique tecnicamente o motivo volto a postar, caso contrario quem utiliza esse ORM faça sempre uma nova instância do contexto para cada operação.

    • Marcado como Resposta galves.rod segunda-feira, 21 de setembro de 2015 18:32
    segunda-feira, 14 de setembro de 2015 18:08