none
Como fazer rollback em Linq to Entities com 2 ObjectContext em bancos diferentes? RRS feed

  • Pergunta

  • Olá, cenário simples:

    Aplicação conecta em 2 bancos de dados e altera informações de ambos.

    Crio 2 ObjectContext e tudo funciona corretamente.

    O problema é se o primeiro ObjectContext gravar as informações e o segundo gerar uma exception.

    Como garantir que ambos ou nenhum vão gravar as informações, se são 2 ObjectContext , cada um conectado em um banco de dados diferente?

    O meu código está assim

    ObjectContextBanco1 dc1 = new ObjectContextBanco1();
    ObjectContextBanco2 dc2 = new ObjectContextBanco2();
    
    //altero dc1 e dc2
    
    dc1.SaveChanges();
    dc2.SaveChanges();
    
    //Se o dc2 der exceção fico com o banco de dados dc1 alterado e o dc2 não.
    //Como reverter as alterações em dc1 ou garantir que caso dc2 falhe dc1 não grave?


    sexta-feira, 27 de julho de 2012 13:34

Respostas

  • Olá Daniel,

    Nunca havia enfrentado esta mensagem de erro.

    Pesquisei e achei diferentes motivos para esta exception. Encontrei links afirmando que é um problema de TimeOut da transação (algo por volta de 60 segundos), também vi que pode ser um problema de COM+, e também achei registros que pode ser um problema de tentar executar algum comportamento com a transação já marcada como completa.

    O que eu recomendo é: tente olhar a propriedade InnerException e ver se existem mais detalhes sobre o erro.

    Outra questão, tente executar um pequeno Insert, Update ou Delete com este escopo de transação, para validar se não é um problema de ambiente.

    []s!


    Fernando Henrique Inocêncio Borba Ferreira
    Microsoft MVP - Data Platform Development
    while(alive){ this.WriteCode(); }
    Blog: http://ferhenriquef.com/
    Twitter: @ferhenrique
    Entity Framework - Brasil: https://www.facebook.com/EntityFrameworkBrasil

    • Marcado como Resposta Daniel Ianegitz sexta-feira, 3 de agosto de 2012 11:14
    terça-feira, 31 de julho de 2012 12:13
    Moderador
  • Olá Daniel,

    Pelo o que eu li neste link: http://msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx Parece que existe um contrutor do TransactionScope que aceita um TimeSpan, onde vc diz o qual o timeout da sua transação.

    []s!


    Fernando Henrique Inocêncio Borba Ferreira
    Microsoft MVP - Data Platform Development
    while(alive){ this.WriteCode(); }
    Blog: http://ferhenriquef.com/
    Twitter: @ferhenrique
    Entity Framework - Brasil: https://www.facebook.com/EntityFrameworkBrasil

    • Marcado como Resposta Daniel Ianegitz sexta-feira, 3 de agosto de 2012 11:13
    quarta-feira, 1 de agosto de 2012 01:10
    Moderador
  • Agradeço a ajuda Fernando Henrique, funcionou com o timeout.

    Testei provocando uma exception no db2 e o db1 não gravou as informações (coisa que antes acontecia) e o timeout não estourou mesmo com um processo de 40 minutos e 10.000 registros (sem definir o timeout gerava exception já com poucos registros, cerca de 300)

    O método que usei foi:

    //TimeSpan zerado = timeout infinito

    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0,0,0))) { ObjectContext1 db1 = new ObjectContext1(); ObjectContext2 db2 = new ObjectContext2(); //meu codigo db1.SaveChanges(); db2.SaveChanges(); scope.Complete(); }




    sexta-feira, 3 de agosto de 2012 11:12

Todas as Respostas

  • Olá Daniel,

    Vc já ouviu falar da classe TransactionScope?

    Provavelmente ela irá atender sua necessidade:

    using (TransactionScope scope = new TransactionScope())  
    {  
            using (ObjectContextBanco1 context = new ObjectContextBanco1())  
            {  
            	// Seu código 
                    context.SaveChanges();  
            }  
      
            using (ObjectContextBanco2 context = new ObjectContextBanco2())  
            {  
            	// Seu código 
                    context.SaveChanges();  
            }  
    	scope.Complete();  
    }  

    []s!


    Fernando Henrique Inocêncio Borba Ferreira
    while(alive){ this.WriteCode(); }
    Blog: http://ferhenriquef.com/
    Twitter: @ferhenrique
    Entity Framework - Brasil: https://www.facebook.com/EntityFrameworkBrasil

    domingo, 29 de julho de 2012 13:15
    Moderador
  • Estou usando VS2008 framework 3.5

    Essa classe existe nesse framework? Não estou localizando o namespace.

    Isso resolveria meu problema mas espero que não seja exclusivo do framework 4.0 e acima.


    segunda-feira, 30 de julho de 2012 15:20
  • Olá Daniel,

    Ela existe a partir do .Net Framework 3.

    http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.90%29.aspx

    Procure pela System.Transactions.dll

    []s!


    Fernando Henrique Inocêncio Borba Ferreira
    Microsoft MVP - Data Platform Development
    while(alive){ this.WriteCode(); }
    Blog: http://ferhenriquef.com/
    Twitter: @ferhenrique
    Entity Framework - Brasil: https://www.facebook.com/EntityFrameworkBrasil

    segunda-feira, 30 de julho de 2012 16:07
    Moderador
  • Encontrei o namespace e adicionei a classe mas agora o mesmo código que antes funcionava agora gera exception:

    Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.

    O meu cenário é um pouco mais complexo, eu não posso fazer separado as 2 interações pois um banco interage com o outro.

    //Nao posso utilizar dessa forma pois context1 e context2 precisam estar juntos, um banco interage com o outro
    using (TransactionScope scope = new TransactionScope())  
    {  
            using (ObjectContextBanco1 context = new ObjectContextBanco1())  
            {  
            	// Seu código 
                    context.SaveChanges();  
            }  
      
            using (ObjectContextBanco2 context = new ObjectContextBanco2())  
            {  
            	// Seu código 
                    context.SaveChanges();  
            }  
    	scope.Complete();  
    }  
    
    
    //Meu código é assim:
    
    ObjectContextBanco1 context1 = new ObjectContextBanco1();
    ObjectContextBanco2 context2 = new ObjectContextBanco2();
    
    int maxId = (from y in context1.tb1                                        select new
    {
       y.id
    }).Max(z => z.id);
    
    var registros = (from y in context2.tb2
    where y.id != maxId
    select new
    {
       y.descricao
    });
    
    //update context1
    //update context2
    
    context1.SaveChanges();
    context2.SaveChanges();
    
    
    

    terça-feira, 31 de julho de 2012 11:38
  • Olá Daniel,

    Nunca havia enfrentado esta mensagem de erro.

    Pesquisei e achei diferentes motivos para esta exception. Encontrei links afirmando que é um problema de TimeOut da transação (algo por volta de 60 segundos), também vi que pode ser um problema de COM+, e também achei registros que pode ser um problema de tentar executar algum comportamento com a transação já marcada como completa.

    O que eu recomendo é: tente olhar a propriedade InnerException e ver se existem mais detalhes sobre o erro.

    Outra questão, tente executar um pequeno Insert, Update ou Delete com este escopo de transação, para validar se não é um problema de ambiente.

    []s!


    Fernando Henrique Inocêncio Borba Ferreira
    Microsoft MVP - Data Platform Development
    while(alive){ this.WriteCode(); }
    Blog: http://ferhenriquef.com/
    Twitter: @ferhenrique
    Entity Framework - Brasil: https://www.facebook.com/EntityFrameworkBrasil

    • Marcado como Resposta Daniel Ianegitz sexta-feira, 3 de agosto de 2012 11:14
    terça-feira, 31 de julho de 2012 12:13
    Moderador
  • Eu também encontrei o post relatando problema com a COM+ e as queryes dele.

    O que acontece é que quando se trata algo via transactionscope quase todas exception geram essa Inner exception genérica relacionada a transação, mesmo sendo por exemplo um erro de TCP.

    Mas no meu caso realmente é relacionada a transaction pois se eu comento ela funciona, se eu habilito ela gera exception.

    Pode ser algo relacionado a timeout pois quando eu tento com poucos registros, 200 ou 300, funciona, quando aumento pra 3000 (próximo ao meu cenário real) gera exception. O tempo total do processo é uma média de 4 minutos e quando limito os registros é menos de 30 segundos.

    Como eu aumento esse timeout pra testar se realmente é ele?

    terça-feira, 31 de julho de 2012 13:48
  • Olá Daniel,

    Pelo o que eu li neste link: http://msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx Parece que existe um contrutor do TransactionScope que aceita um TimeSpan, onde vc diz o qual o timeout da sua transação.

    []s!


    Fernando Henrique Inocêncio Borba Ferreira
    Microsoft MVP - Data Platform Development
    while(alive){ this.WriteCode(); }
    Blog: http://ferhenriquef.com/
    Twitter: @ferhenrique
    Entity Framework - Brasil: https://www.facebook.com/EntityFrameworkBrasil

    • Marcado como Resposta Daniel Ianegitz sexta-feira, 3 de agosto de 2012 11:13
    quarta-feira, 1 de agosto de 2012 01:10
    Moderador
  • Agradeço a ajuda Fernando Henrique, funcionou com o timeout.

    Testei provocando uma exception no db2 e o db1 não gravou as informações (coisa que antes acontecia) e o timeout não estourou mesmo com um processo de 40 minutos e 10.000 registros (sem definir o timeout gerava exception já com poucos registros, cerca de 300)

    O método que usei foi:

    //TimeSpan zerado = timeout infinito

    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0,0,0))) { ObjectContext1 db1 = new ObjectContext1(); ObjectContext2 db2 = new ObjectContext2(); //meu codigo db1.SaveChanges(); db2.SaveChanges(); scope.Complete(); }




    sexta-feira, 3 de agosto de 2012 11:12