Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

Como trabalhamos com Transações no LINQ?

Oi Marcelo, bom dia. Muito bom o curso. Porém, fiquei curiosa para saber como trabalhar com transações no LINQ x Entity? Exemplo:

Estou cadastrando um faturamento e para tal preciso inserir a Nota Fiscal e seus respectivos itens dessa nota fiscal.

Como elas ficam em tabelas distintas, caso algum problema ocorra, não posso gravar em só uma das duas tabelas do banco. certo? Não posso ter nota fiscal sem item e não posso ter item sem nota fiscal.

Como controlo isso no Entity? Uso transação? Como é a sintaxe disso?

4 respostas
solução!

Olá, Vanessa, obrigado, fico feliz que esteja gostando do curso!

Sobre transações: Você pode usar SqlConnection e seu método BeginTransaction para iniciar uma transação e dentro dela realizar essas operações inseparáveis, como gravar nota fiscal e gravar seus itens.

Felizmente, o EF permite que você adicione uma entidade e todas as suas dependências, contanto que você tenha definido corretamente as propriedades de navegação no modelo. Ex: uma entidade NotaFiscal e outra ItemNotaFiscal, sendo que esta última tem uma propriedade que aponta para a entidade de NF, e na entidade NotaFiscal você tem uma propriedade List<ItemNotaFiscal> Itens. Neste caso, você pode:

  • Criar uma nova instância de NotaFiscal
  • Popular a NotaFiscal.Itens com todos os itens da nota
  • Adicionar ao contexto com context.NotasFiscais.Add(...)
  • Salvar com SaveChanges

Note que essa nova nota fiscal ainda não existe no banco de dados, muito menos os itens dela. Então, quando você adiciona essa NF e salva com SaveChanges, o EF reconhece que todos esses objetos novos devem ser salvos no banco de dados. Então, em vez de salvar no EF em duas etapas, você pode criar uma nota + itens e salvar uma vez só.

Em DDD (Domain Driven Design) essa entidade Nota Fiscal se encaixa no conceito conhecido como "aggregate root" (= "raiz agregada").

Vou deixar aqui embaixo um código de exemplo (não testado) que você poderia usar, mas deixo por sua conta pra você implementar o método GetNotaFiscalComItens ;)

Boa sorte e bons estudos!

using (var conn = new SqlConnection("...")) 
{ 
   conn.Open(); 

   using (var sqlTransaction = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot)) 
   { 
       try 
       { 
           using (var context =  
             new MyContext(conn, contextOwnsConnection: false)) 
            { 
                context.Database.UseTransaction(sqlTransaction); 

                NotaFiscal notaFiscal = GetNotaFiscalComItens(); // crie um método para fornecer a NF + itens

                context.NotasFiscais.Add(notaFiscal); // nota fiscal já contendo todos os itens
                context.SaveChanges();
            } 

            sqlTransaction.Commit(); 
        } 
        catch (Exception) 
        { 
            sqlTransaction.Rollback(); 
        } 
    } 
}

Marcelo, Pelo que eu entendi tenho duas formas então de trabalhar estas questões que envolvem integridade, certo?

Uma com DDD e outra com transaction, mas tenho que usar o SQLconnection no segundo caso. É isso?

Se for isso mesmo, vou investir em aprender essa forma de trabalhar com Entity que permite popular a entidade com as suas sub-tabelas relacionadas (itens, fornecedores, clientes e etc) e gravar tudo no mesmo momento no banco. Está certo o meu entendimento? Vou exercitar mais por aqui. Valeu!!

Oi Vanessa!

Agora estou vendo que a minha resposta lá em cima não estava completa. Na verdade, o EF já cria uma transação automaticamente.

Em todas as versões do Entity Framework, sempre que você executa SaveChanges() para inserir, atualizar ou excluir no banco de dados, o framework envolve aquela operação numa transação. Essa transação dura somente o tempo necessário para executar a operação e então a complete. Quando você executa outra operação, uma nova transação é iniciada.

Ou seja, para o exemplo que coloquei lá em cima, não seria necessário eu criar uma transação explicitamente: o EF cuidaria de tudo automaticamente.

O uso de SqlTransaction no EF é mais indicado para situações em que uma das operações é feita "por fora" das entidades, como num comando UPDATE direto no banco de dados, por exemplo:

https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx

using System; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Data.SqlClient; 
using System.Linq; 
using System.Transactions; 

namespace TransactionsExamples 
{ 
    class TransactionsExample 
    { 
        static void StartOwnTransactionWithinContext() 
        { 
            using (var context = new BloggingContext()) 
            { 
                using (var dbContextTransaction = context.Database.BeginTransaction()) 
                { 
                    try 
                    { 
                        context.Database.ExecuteSqlCommand( 
                            @"UPDATE Blogs SET Rating = 5" + 
                                " WHERE Name LIKE '%Entity Framework%'" 
                            ); 

                        var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
                        foreach (var post in query) 
                        { 
                            post.Title += "[Cool Blog]"; 
                        } 

                        context.SaveChanges(); 

                        dbContextTransaction.Commit(); 
                    } 
                    catch (Exception) 
                    { 
                        dbContextTransaction.Rollback(); 
                    } 
                } 
            } 
        } 
    } 
}

Nesse exemplo de cima, temos um bom uso do SQLTransaction!

Agora, existe um outro tipo de transação, com o objeto TransactionScope, que é mais indicado para operações entre diferentes servidores SQL Server, por exemplo. O uso de diferentes servidores numa transação exige o MSDTC (Microsoft Distributed Transaction Coordinator, ou Coordenador de Transações Distribuídas).

https://msdn.microsoft.com/pt-br/library/system.transactions.transactionscope(v=vs.110).aspx

Boa sorte e bons estudos!

Entendi. É Marcelo, é um novo mundo para mim. Uma quebra de paradigma. Desenvolvo há mais 20 anos e estou travando em questões básicas com o Entity e o Linq. Mas vou chegar lá. É um recomeço, faz parte. Já passei por outros assim. Vamos lá. Valeu! A bola dá vez agora é fazer funcionar o add-migrations. Não consigo atualizar o banco. Somente usando o edmx. Com o CodeFirst nada... o add-migrations dá erro.