Conflict between objects of the same type in the Entity Framework

0

I have the following class:

public class Conteudo
{
    public long Id { get; set; }
    public string Categoria { get; set; }
    public string SubCategoria { get; set; }
    public string Descricao { get; set; }

    public string Status { get; set; }
    public string Obs { get; set; }
    public string Evidencias { get; set; }
    public string NSerie { get; set; }

    [ForeignKey("UsuarioAbertura")]
    public long? UsuarioAberturaId { get; set; }
    public virtual Usuario UsuarioAbertura { get; set; }

    [ForeignKey("UsuarioFechamento")]
    public long? UsuarioFechamentoId { get; set; }
    public virtual Usuario UsuarioFechamento { get; set; }

    public DateTime? DataHoraAbertura { get; set; }
    public DateTime? DataHoraFechamento { get; set; }


    public long ManifestacaoId { get; set; }
    [ForeignKey("ManifestacaoId")]
    public virtual Manifestacao Manifestacao { get; set; }
}

There are two types of User : UserOpen and UserOpen. One opens the Content and another closes.

The problem is that if the user who closes is different from the one who opened the Content, when trying to read the database, the following error is displayed:

System.ObjectDisposedException: 'The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.'

When the same user who opens and closes, no error is displayed.

User class:

public class Usuario
{
    public long Id { get; set; }
    public string Apelido { get; set; }
    public string Nome { get; set; }
    public string Categoria { get; set; }
    public string Senha { get; set; }
    public string Status { get; set; }

    public virtual ICollection<Conteudo> ConteudosAbertura { get; set; }

    public virtual ICollection<Conteudo> ConteudosFechamento { get; set; }
}

Context class:

public class EFContext : DbContext
{
    public EFContext() : base("Pos_Venda_SAC")
    {
        //this.Configuration.LazyLoadingEnabled = false;
        Database.SetInitializer<EFContext>(
        new DropCreateDatabaseIfModelChanges<EFContext>()
        );
    }
    public DbSet<Cliente> Clientes { get; set; }
    public DbSet<Conteudo> Conteudos { get; set; }
    public DbSet<Manifestacao> Manifestacoes { get; set; }
    public DbSet<Usuario> Usuarios { get; set; }
    public DbSet<Destinatario> Destinatarios { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Usuario>()
            .HasMany<Conteudo>(c => c.ConteudosAbertura)
            .WithOptional(c => c.UsuarioAbertura)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Usuario>()
            .HasMany<Conteudo>(c => c.ConteudosFechamento)
            .WithOptional(c => c.UsuarioFechamento)
            .WillCascadeOnDelete(false);

    }
}

ADDED:

Code that generates the error:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.OleDb;
using PosVendaSAC.POCO;
using PosVendaSAC.Classes;
using PosVendaSAC.Contexts;

namespace PosVendaSAC
{
    public partial class FrmDetalhesConteudo : Form
    {
        DAL_Conteudos dalConteudos = new DAL_Conteudos();
        DAL_Usuario dalUsuario = new DAL_Usuario();
        Conteudo conteudoAtual = new Conteudo();
        private string nomeUsuario;

        private bool textoEmailAlterado;
        private bool textoTelefoneAlterado;
        private bool textoRamalAlterado;
        private bool textoCelularAlterado;
        private bool textoContatoAlterado;

        public FrmDetalhesConteudo(long idRow)
        {
            InitializeComponent();
            nomeUsuario = UsuarioLogado.Nome;
            conteudoAtual = dalConteudos.GetConteudoByID(idRow);
            PreencheCampos(conteudoAtual);
            DesabilitarEdicaoCampos();
            VerificarStatusFinalizado();
            textoEmailAlterado = false;
            textoTelefoneAlterado = false;
            textoRamalAlterado = false;
            textoCelularAlterado = false;
            textoContatoAlterado = false;
        }

        private void PreencheCampos(Conteudo conteudo)
        {
            txtEmpresaCliente.Text = conteudo.Manifestacao.Cliente.Empresa;
            txtContatoCliente.Text = conteudo.Manifestacao.Cliente.Contato;
            txtEmailCliente.Text = conteudo.Manifestacao.Cliente.Email;
            mskTelefoneCliente.Text = conteudo.Manifestacao.Cliente.Telefone;
            txtRamalCliente.Text = conteudo.Manifestacao.Cliente.Ramal;
            mskCelularCliente.Text = conteudo.Manifestacao.Cliente.Celular;
            txtCategoria.Text = conteudo.Categoria;
            txtSubCategoria.Text = conteudo.SubCategoria;
            txtConteudo.Text = conteudo.Descricao;
            txtStatus.Text = conteudo.Status;
            txtObs.Text = conteudo.Obs;
            txtEvidencias.Text = conteudo.Evidencias;
            lblTimeAbertura.Text = conteudo.DataHoraAbertura.ToString();
            lblTimeFechamento.Text = conteudo.DataHoraFechamento.ToString();
            lblUsuAbertura.Text = conteudo.UsuarioAbertura.Nome.ToString();
            txtNChamado.Text = conteudo.Manifestacao.NumeroChamado;
            txtNSerie.Text = conteudo.NSerie;
        }

        private void VerificarStatusFinalizado()
        {
            if (txtStatus.Text == "Finalizado")
            {
                if (!UsuarioLogado.IsEng())
                {
                    cboNovoStatus.Enabled = false;
                    cboNovoStatus.Text = "Status finalizado não pode ser alterado";
                }
                lblTimeFechamento.Visible = true;
                lblTitFechamento.Visible = true;
                lblTitIntervalo.Visible = true;
                lblDiasIntervalo.Visible = true;
                lblTitUsuFechamento.Visible = true;
                lblUsuFechamento.Text = conteudoAtual.UsuarioFechamento.Nome;
                lblUsuFechamento.Visible = true;
                CalculaIntervalo();
            }
        }

        private void DesabilitarEdicaoCampos()
        {
            foreach (Control ctrl in gbxConteudo.Controls)
            {
                if (ctrl is TextBox)
                {
                    ((TextBox)ctrl).ReadOnly = true;
                }
            }

            txtNSerie.ReadOnly = false;
            txtEmpresaCliente.ReadOnly = true;
            txtStatus.ReadOnly = true;
        }

        private void cmdSalvar_Click(object sender, EventArgs e)
        {
            //VerificaAlteracaoDadosContato();
            VerificaAlteracaoStatus();
            System.Threading.Thread.Sleep(200);
            this.Close();
        }

        private void VerificaAlteracaoStatus()
        {
            if (cboNovoStatus.Text == "Finalizado")
            {
                DialogResult dialogResult = MessageBox.Show("Confirma a alteração do status para 'Finalizado'? Após finalizado um registro não pode mais ser alterado.", "Importante!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (dialogResult == DialogResult.Yes)
                {
                    UpdateConteudo();
                    System.Threading.Thread.Sleep(200);
                    VerificarStatusFinalizado();
                    NotificaTermino();
                    MessageBox.Show("Registro atualizado com sucesso.");
                }
                else
                {
                    return;
                }
            }
            else
            {
                UpdateConteudo();
            }
        }

        private void UpdateConteudo()
        {
            using (var context = new EFContext())
            {
                var newConteudo = context.Conteudos.Find(conteudoAtual.Id);
                if (cboNovoStatus.Text == "Finalizado")
                {
                    newConteudo.DataHoraFechamento = PegaDataHoraUsuario.DataHoraPadrao();
                    newConteudo.UsuarioFechamentoId = UsuarioLogado.Id;
                }
                if (cboNovoStatus.SelectedIndex > -1)
                {
                    newConteudo.Status = cboNovoStatus.Text;
                }
                newConteudo.Obs = txtObs.Text;
                newConteudo.Evidencias = txtEvidencias.Text;
                newConteudo.NSerie = txtNSerie.Text;
                context.SaveChanges();
            }
        }

        private void UpdateDadosContato()
        {
            using (var context = new EFContext())
            {
                var newContato = context.Clientes.Find(conteudoAtual.Manifestacao.Cliente.Id);
                newContato.Email = txtEmailCliente.Text;
                newContato.Celular = mskCelularCliente.Text;
                newContato.Ramal = txtRamalCliente.Text;
                newContato.Telefone = mskTelefoneCliente.Text;
                newContato.Contato = txtContatoCliente.Text;
                context.SaveChanges();
            }
        }

        private void NotificaTermino()
        {
            string procedimento = "fechamento";
            string dataHora = PegaDataHoraUsuario.DataHoraInvertida();

            string corpoEmailString = "<p><strong><font size='4'>Manifestação Finalizada</font></p>";
            corpoEmailString += "<p>Chamado: " + txtNChamado.Text + "</p>";
            corpoEmailString += "<p><strong><font size='3'>Empresa: " + txtEmpresaCliente.Text + "</font></p>";
            corpoEmailString += "<p><font size='2'> Conteúdo " + ": </font> </p>";
            corpoEmailString += "<p>Categoria: " + txtCategoria.Text + "</p>";
            corpoEmailString += "<p>Subcategoria: " + txtSubCategoria.Text + "</p>";
            corpoEmailString += "<p>Conteúdo: " + txtConteudo.Text + "</p>";
            corpoEmailString += "<br/>";
            corpoEmailString += "<p>Contato: " + txtContatoCliente.Text + "</p>";
            corpoEmailString += "<p>e-mail: " + txtEmailCliente.Text + "</p>";
            corpoEmailString += "<p>Telefone: " + mskTelefoneCliente.Text + "</p>";
            corpoEmailString += "<p>Ramal: " + txtRamalCliente.Text + "</p>";
            corpoEmailString += "<p>Celular: " + mskCelularCliente.Text + "</p>";
            corpoEmailString += "<p>Conteúdo encerrado por: " + nomeUsuario + "</p>";
            corpoEmailString += "<p>Data e hora do encerramento: " + lblTimeFechamento.Text + "</p>";
            corpoEmailString += "<p>Aberto em: " + lblTimeAbertura.Text + " por: " + lblUsuAbertura.Text + "</p>";
            corpoEmailString += "<p>Tempo decorrido até o fechamento: " + lblDiasIntervalo.Text + "</p>";

            List<string> vazio = new List<string>() { "", "" };

            string resultado = SendMail.EnviaMensagemEmail(StringsProjeto.remetente, "Manifestação Finalizada", corpoEmailString, procedimento, vazio);
        }    

        private void CalculaIntervalo()
        {
            DateTime fechamento = Convert.ToDateTime(lblTimeFechamento.Text.ToString());
            DateTime abertura = Convert.ToDateTime(lblTimeAbertura.Text.ToString());
            TimeSpan ts1 = fechamento.Subtract(abertura);
            string intervaloDias = ts1.ToString("dd");
            string intervaloHoras = ts1.ToString("hh");
            lblDiasIntervalo.Text = intervaloDias + " dias e " + intervaloHoras + " horas ";

        }

        private void txtEmailCliente_TextChanged(object sender, EventArgs e)
        {
            textoEmailAlterado = true;
        }

        private void mskTelefoneCliente_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
        {
            textoTelefoneAlterado = true;
        }

        private void txtRamalCliente_TextChanged(object sender, EventArgs e)
        {
            textoRamalAlterado = true;
        }

        private void mskCelularCliente_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
        {
            textoCelularAlterado = true;
        }

        private void txtContatoCliente_TextChanged(object sender, EventArgs e)
        {
            textoContatoAlterado = true;
        }
    }
}
    
asked by anonymous 20.10.2017 / 16:04

1 answer

2

Let's face it, even though your DAL is masking the problem, I believe the GetConteudoByID(id) method is doing something of the genre.:

using (var context = new EFContext())
{
    return context.Conteudos.Find(id);
}

When you do this, the connection is being closed when Dispose() of context is called, just as the EF context is destroyed.

Now let's look at the following line.:

conteudoAtual.UsuarioFechamento.Nome

As during the query of the content, you did not EagerLoad , then it will be consulted only when it is accessed, making a LazyLoad , as the context has already been discarded, then the connection is no longer available. .

What you can do in the short term is to change your GetConteudoByID(id) method to DAL to.:

using (var context = new EFContext())
{
    return context.Conteudos
        .Include(u => u.UsuarioAbertura)
        .Include(u => u.UsuarioFechamento)
        .Include(u => u.Manifestacao)
        .FirstOrDefault(u => u.Id == id);
}

In the long run, I advise you to abandon this approach with DAL , you are only artificially limiting EF , in the long run you will see that in addition to not bringing benefits, some problems will be introduced.

If you really want to reuse GetConteudoByID(id) , then make an extension for your EFContext , and do not be afraid to access the EFContext direct on the form.

public static class DAL_Conteudos
{
    public static Conteudo GetConteudoByID(this EFContext context, int id)
    {
        return context.Conteudos
            .Include(u => u.UsuarioAbertura)
            .Include(u => u.UsuarioFechamento)
            .Include(u => u.Manifestacao)
            .FirstOrDefault(u => u.Id == id);
    }
}

Believe it, it's not ugly to do.:

public partial class FrmDetalhesConteudo : Form
{        
    EFContext context = new EFContext();
    Conteudo conteudoAtual = new Conteudo();

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            context?.Dispose();
        }
        base.Dispose(disposing);
    }

    public FrmDetalhesConteudo(long idRow)
    {
        InitializeComponent();
        nomeUsuario = UsuarioLogado.Nome;
        conteudoAtual = context.GetConteudoByID(idRow);
        PreencheCampos(conteudoAtual);
        DesabilitarEdicaoCampos();
        VerificarStatusFinalizado();
        textoEmailAlterado = false;
        textoTelefoneAlterado = false;
        textoRamalAlterado = false;
        textoCelularAlterado = false;
        textoContatoAlterado = false;
    }

    private void VerificarStatusFinalizado()
    {
        if (txtStatus.Text == "Finalizado")
        {
            if (!UsuarioLogado.IsEng())
            {
                cboNovoStatus.Enabled = false;
                cboNovoStatus.Text = "Status finalizado não pode ser alterado";
            }
            lblTimeFechamento.Visible = true;
            lblTitFechamento.Visible = true;
            lblTitIntervalo.Visible = true;
            lblDiasIntervalo.Visible = true;
            lblTitUsuFechamento.Visible = true;
            lblUsuFechamento.Text = conteudoAtual.UsuarioFechamento.Nome;
            lblUsuFechamento.Visible = true;
            CalculaIntervalo();
        }
    }
}

If you'd like to know more about Por que não é legal usar DAL/Repository com EF :

When to use Entity Framework with Repository Pattern?

    
20.10.2017 / 18:51