none
Entity Framework- Duvidas para criar instancias do Banco. RRS feed

  • Pergunta

  • Bom eu estou desenvolvendo uma aplicação usando o EF com sqlserver , me surgiu uma duvida quando cheguei a certo ponto do sistema , eu tenho todas as funções divididas por classes, classe de consulta cliente , de cadastra cliente por aí vai , em cada uma das classes eu crio uma instancia do banco, e também tenho no meu banco tabelas para poder ter um histórico de usuários e suas ações no sistema. Então ao fim de cada operação chamo minha classe de histórico e gravo o usuário que fez a operação e o tipo de operação, mas em uma de minhas funções eu tenho que fazer um loop de operações , e a cada loop eu gravo um registro de histórico , mas se um desses falhar eu tenho que parar tudo e fazer um rollBack para desfazer todas as operações, mas quando faço o roll back ele não desfaz os registros de histórico , pq eu estou chamando a função de histórico de uma outra classe que está usando uma outra instancia do banco, aí nessa caso vou ter que fazer a função de log junto com essa função que gera varias operações em loop, aí a função fica gigante e ilegível.

    Será que se eu fazer um singleton para garantir que eu use sempre a mesma instancia é uma boa pratica usando EF ? 

    quinta-feira, 5 de novembro de 2015 11:26

Respostas

  • Vinicius,

    você não precisa ficar dando SaveChanges() a cada laço do for ou foreach...você vai adicionando os dados que precisa com o método Add(entidade) e ao término da operação, você usa o SaveChanges() uma única vez, ele vai salvar toda a transação, ou melhor, salvar todo o contexto.

    Escrevi um artigo para o blog .NET Coders falando sobre o EF, veja na parte dois do artigo que eu faço 'n' operações no contexto e no final uso o SaveChanges() uma única vez!

    Mapeamento com Entity Framework Code-First (Fluent-Api)


    quinta-feira, 5 de novembro de 2015 14:54
  • Vinicius, criei um exemplo melhor para resolver o problema utilizando as propriedades de navegação, assim tu não precisas se preocupar com os contextos.

    Entidades:

    public class Cliente
        {    
            public int Id { get; set; }
            public string Nome { get; set; }
        
            public virtual ICollection<HistoricoCliente> HistoricoCliente { get; set; }
        }
    public class HistoricoCliente
        {
            public int Id { get; set; }
            public string Descricao { get; set; }
            public int ClienteId { get; set; }
        
            public virtual Cliente Cliente { get; set; }
        }

    Métodos:

    public class HistoricoService
        {
            public static HistoricoCliente GeraHistorico(Cliente cliente)
            {
                return new HistoricoCliente
                {
                    Cliente = cliente,
                    Descricao = "Descrição do histórico do cliente " + cliente.Nome
                };
            }
        }
    public class ClienteService
        {
            public static void InserirClientes(List<Cliente> clientes)
            {
                using (DBContextContainer ctx = new DBContextContainer())
                {
                    try
                    {
                        foreach (var item in clientes)
                        {
                            item.HistoricoCliente.Add(HistoricoService.GeraHistorico(item));
                            ctx.ClienteSet.Add(item);
                        }
    
                        ctx.SaveChanges();
                    }
                    catch (Exception)
                    {
                        // Operação negada
                    }
                }
            }
        }

    Com o Entity, tu não precisas do ID para gerar uma relação; usando as propriedades de navegação tu consegues gravar em mais de uma tabela, utilizando o mesmo Contexto.Add().

    Ao final de toda operação, caso nada tenha dado errado, ele salva as mudanças.



    Att., Rafael Simor

    quinta-feira, 5 de novembro de 2015 14:30

Todas as Respostas

  • Bom dia Vinicius,

    Não entendi direito o que você disse em relação a instâncias, acredito que você queira se referenciar a uma sessão no banco, que a cada chamada que você faz abre uma nova sessão.

    Bom, seguindo essa linha de pensamento, está simples de resolver o seu problema.

    Dentro do seu loop tem algum lugar que você está chamando o SaveChanges() dentro do método ou métodos que o loop chama (provavelmente na sua camada de repositório).

    Basta você retirar essa chamada do SaveChanges() do método que é chamado e colocá-lo fora do loop, assim se der algum erro, o SaveChanges() que é quem faz o Commit no banco não será chamado e assim dará o rollback.

    Pra fazer essa chamada do SaveChanges caso esteja em uma camada separada de Repositório você pode criar um método SaveChanges() na camada do Repositório em que o seu método que faz o Loop consiga enxergar. 

    Qualquer dúvida estou à disposição.

    quinta-feira, 5 de novembro de 2015 11:38
  • Bom dia.

    Entendi sua dúvida.

    Tu tens duas classes/métodos, cada um instanciando um contexto diferente, então ao salvar um Histórico, ele vai instanciar e fechar este contexto ao termino da operação, correto?

    Assim, não sei se seria uma boa prática, mas para não ter que mudar toda a estrutura do teu projeto, tu não poderias passar o contexto por parâmetro do método de inserir no histórico?

    public void GravaHistorico(DbContext context = null)

    Assim, se tu passares um contexto por parâmetro, ele usará esse contexto (o mesmo contexto será usado para todas operações), senão, instanciará um novo.

    Tu terias só que mudar tua lógica de gravação. Tu poderias usar um SaveChanges(false) e, caso todos registros sejam inseridos com sucesso, chama o .AcceptAllChanges.

    Apesar de achar que isso funcionaria, também fico no aguardo de uma resposta melhor estruturada. :)


    Att., Rafael Simor


    • Editado SimorC quinta-feira, 5 de novembro de 2015 12:44
    quinta-feira, 5 de novembro de 2015 12:43
  • Cara o jeito que vc descreveu é como eu estou fazendo, mas eu não posso tirar os saves changes ,pq eu começo gravando a tabela1, aí para gerar uma outra operação eu preciso dessa id da tabela1 então por isso eu preciso do savechange() dentro do for, Eu só queria saber se tem problema eu ficar usando a mesma instancia.

     


    quinta-feira, 5 de novembro de 2015 12:53
  • Vinicius, criei um exemplo melhor para resolver o problema utilizando as propriedades de navegação, assim tu não precisas se preocupar com os contextos.

    Entidades:

    public class Cliente
        {    
            public int Id { get; set; }
            public string Nome { get; set; }
        
            public virtual ICollection<HistoricoCliente> HistoricoCliente { get; set; }
        }
    public class HistoricoCliente
        {
            public int Id { get; set; }
            public string Descricao { get; set; }
            public int ClienteId { get; set; }
        
            public virtual Cliente Cliente { get; set; }
        }

    Métodos:

    public class HistoricoService
        {
            public static HistoricoCliente GeraHistorico(Cliente cliente)
            {
                return new HistoricoCliente
                {
                    Cliente = cliente,
                    Descricao = "Descrição do histórico do cliente " + cliente.Nome
                };
            }
        }
    public class ClienteService
        {
            public static void InserirClientes(List<Cliente> clientes)
            {
                using (DBContextContainer ctx = new DBContextContainer())
                {
                    try
                    {
                        foreach (var item in clientes)
                        {
                            item.HistoricoCliente.Add(HistoricoService.GeraHistorico(item));
                            ctx.ClienteSet.Add(item);
                        }
    
                        ctx.SaveChanges();
                    }
                    catch (Exception)
                    {
                        // Operação negada
                    }
                }
            }
        }

    Com o Entity, tu não precisas do ID para gerar uma relação; usando as propriedades de navegação tu consegues gravar em mais de uma tabela, utilizando o mesmo Contexto.Add().

    Ao final de toda operação, caso nada tenha dado errado, ele salva as mudanças.



    Att., Rafael Simor

    quinta-feira, 5 de novembro de 2015 14:30
  • Vinicius,

    você não precisa ficar dando SaveChanges() a cada laço do for ou foreach...você vai adicionando os dados que precisa com o método Add(entidade) e ao término da operação, você usa o SaveChanges() uma única vez, ele vai salvar toda a transação, ou melhor, salvar todo o contexto.

    Escrevi um artigo para o blog .NET Coders falando sobre o EF, veja na parte dois do artigo que eu faço 'n' operações no contexto e no final uso o SaveChanges() uma única vez!

    Mapeamento com Entity Framework Code-First (Fluent-Api)


    quinta-feira, 5 de novembro de 2015 14:54