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

Lição: SaveChanges com filtros

Olá,

Estou seguindo as lições do Victor, porém o que ele pede está gerando uma exceção na classe SaveChangesFilter no método OnActionExecuted

 public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // grava modificações e fecha o contexto
            if (filterContext.Exception == null)
            {
                // se não ocorreu nenhum erro, então grave as modificações
                contexto.SaveChanges();
            }
            contexto.Dispose();
        }

A exceção é: The operation cannot be completed because the DbContext has been disposed.

Exception Details: System.InvalidOperationException: The operation cannot be completed because the DbContext has been disposed.

Então, Dispose ou não Dispose?

A minha única solução foi retirar o contexto.Dispose(), mas o Victor pelo visto usou sem problemas de acordo com resultado do exercício.Alguém tem alguma sugestão do porque ocorreu isso comigo? fiz bem em tirar o dispose ou deveria seguir a resolução do exercício, pois o problema deve estar em outro local?

Obrigada!

5 respostas

Oi Naiade

Você poderia postar o código completo do seu filtro e dos seus DAOs?

O problema do código é que o SaveChanges precisa ser chamado em um contexto que ainda não foi fechado, ou seja, o Dispose ainda não foi chamado. No seu caso o Entity Framework está jogando uma exceção por que em algum outro ponto da request o Dispose está sendo chamado antes do SaveChanges.

Bom dia Victor, ainda não consegui descobrir o problema. Segue abaixo os códigos:

SaveChangesFilter

 public class SaveChangesFilter : System.Web.Mvc.ActionFilterAttribute
    {
        private EntidadesContext contexto;

        public SaveChangesFilter(EntidadesContext contexto)
        {
            this.contexto = contexto;
        }
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // grava modificações e fecha o contexto
            if (filterContext.Exception == null)
            {
                // se não ocorreu nenhum erro, então grave as modificações
                contexto.SaveChanges();
            }
            contexto.Dispose();
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            if (filterContext.Exception == null)
            {
                this.contexto.SaveChanges();
            }
            this.contexto.Dispose();
        }
    }

ProdutosController

 private ProdutosDAO dao;

public ProdutosController(ProdutosDAO dao)
{
    this.dao = dao;
}

//Outras ações ...

public ActionResult Adiciona(Produto produto)
{
   dao.Adiciona(produto);
   return RedirectToAction("Index");
}

NinjectWebCommon

 private static void RegisterServices(IKernel kernel)
 {
    // aqui dentro faremos o registro do EntidadesContext
    /*Utilizamos o método Bind do kernel para registrarmos um novo componente
    * Quando algum DAO pedir uma instância de EntidadesContext, queremos devolver o próprio EntidadesContext, para configurarmos isso, utilizamos o método ToSelf 
             Mas queremos um contexto por requisição web, ou seja, o tempo de vida do contexto é de uma request (Request Scope). Para associar o tempo de vida de uma sessão à requisição, utilizamos o método InRequestScope*/

  kernel.Bind<EntidadesContext>().ToSelf().InRequestScope();

  /*
  * Para registrar um filtro, utilizamos o extension method BindFilter do IKernel passando a ordem de execução do filtro e o escopo do filtro (escopo global para um filtro é sempre executado, por exemplo):
   */
    int ordemDeExecucao = 1;
    kernel.BindFilter<SaveChangesFilter>(FilterScope.Global, ordemDeExecucao);
 }

ProdutosDAO

public class ProdutosDAO
    {
        private EntidadesContext contexto;

        public ProdutosDAO(EntidadesContext contexto)
        {
            this.contexto = contexto;
        }

        public void Adiciona(Produto produto)
        {
            this.contexto.Produtos.Add(produto);
        }

        public void Remove(Produto produto)
        {
            this.contexto.Produtos.Remove(produto);
        }

        public void Atualiza(Produto produto)
        {
            this.contexto.Entry(produto).State = System.Data.EntityState.Modified;
        }

        public Produto BuscaPorId(int id)
        {
            return this.contexto.Produtos.Find(id);
        }

        public IEnumerable<Produto> Lista()
        {
            return this.contexto.Produtos;
        }

        public IEnumerable<Produto> ProdutosComPrecoMaiorDoQue(decimal? preco)
        {
            return new List<Produto>();
        }

        public IEnumerable<Produto> ProdutosDaCategoria(string nomeCategoria)
        {
            return new List<Produto>();
        }

        public IEnumerable<Produto> ProdutosDaCategoriaComPrecoMaiorDoQue(decimal? preco, string nomeCategoria)
        {
            return new List<Produto>();
        }

        public IEnumerable<Produto> ListaPaginada(int paginaAtual)
        {
            return new List<Produto>();
        }

        public IEnumerable<Produto> BuscaPorPrecoCategoriaENome(decimal? preco, string nomeCategoria, string nome)
        {
            return new List<Produto>();
        }
    }

Produto

public class Produto
    {
        public int Id { get; set; }

        public string Nome { get; set; }

        public string Descricao { get; set; }

        public decimal Preco { get; set; }

        public virtual Categoria Categoria { get; set; }
        public int? CategoriaID { get; set; }
    }

Meu projeto!

Obrigada,

Naiade Lima

solução!

Oi Naiade

o problema é realmente o Dispose que está no código do método OnActionExecuted. Quando o Asp.Net MVC recebe uma requisição ele a trata executando os filtros e controller na seguinte ordem:

OnActionExecuting > Código do controller > OnActionExecuted > Código da View (cshtml) > OnResultExecuted

Como o código do OnActionExecuted chama o Dispose do contexto, o OnResultExecuted é executado com a conexão para o banco de dados já fechada e por isso o Entity Framework lança a exceção.

Para corrigir o problema, você pode simplesmente remover o Dispose do método OnActionExecuting com isso a conexão vai ser fechada somente pelo OnResultExecuted depois que toda a requisição foi tratada.

Em relação ao curso, acho que ficou um pouco confuso a sua passagem do projeto LojaEF para LojaWebEF e com isso acabamos não percebendo um detalhes.

Aproveita para sugerir que no final das atividades tenha o projeto completo de acordo com as instruções da aula. Assim, caso seja preciso, podemos comparar e identificar os erros.

Ah sim, entendi sua explicação, eu fiquei um pouco perdida nesta lição, mas entendi agora a lógica de como funciona os métodos.

Obrigada, consegui fazer a loja rodar.

Só uma observação! Eu também concordo com o Renato.

Atenciosamente,

Naiade Lima

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software