Error editing registry more than once with EntityFramework C #

0

When editing a record and saving for the first time the entityframework performs the update successfully, however when I click again to edit and save the registry I encounter the following error:

  

Error saving record: Attaching an entity of type   'Project.WebERP.EntityFramework.Entities.Counts.Location.City'   failed because another entity of the same type has already the same   primary key value. This can happen when using the 'Attach' method or   setting the state of an entity to 'Unchanged' or 'Modified' if any   entities in the graph have conflicting key values. This may be because   database-generated key   values. In this case use the 'Add' method or the 'Added' entity state   to track the graph and then set the state of non-new entities to   'Unchanged' or 'Modified' as appropriate.

What am I doing wrong?

Base Repository

public abstract class RepositorioBase<TEntity> : IRepositorioCRUD<TEntity>, IRepositioSQL<TEntity>
    where TEntity : EntityBase
{
    public RepositorioBase()
    {
        _context = new ProjetoContext();
    }

    ProjetoContext _context;

    public long GetNextHandle()
    {
        var instance = Activator.CreateInstance<TEntity>();
        var tabela = instance.GetType().Name.ToUpper();
        var handle = _context.Database.SqlQuery<long>("SELECT (COALESCE(MAX(HANDLE),0) + 1) HANDLE FROM " + tabela).ToArray();
        return Convert.ToInt64(handle[0]);            
    }

    public long Inserir(TEntity entity)
    {
        try
        {
            entity.Handle = GetNextHandle();
            _context.Set<TEntity>().Attach(entity);
            _context.Entry(entity).State = System.Data.Entity.EntityState.Added;

            var erros = _context.GetValidationErrors();
            if (erros.Count() > 0)
            {
                string stringErro = string.Empty;
                foreach (var erro in erros)
                    foreach (var er in erro.ValidationErrors)
                        stringErro += string.Format(er.ErrorMessage.ToString() + " {0}", Environment.NewLine);

                if (!string.IsNullOrEmpty(stringErro))
                    throw new Exception(stringErro);
            }
            return _context.SaveChanges();

        }
        catch (Exception erro)
        {
            throw new Exception(erro.Message);
        }
    }

    public virtual bool Atualizar(TEntity entity)
    {
        try 
        {
            _context.Set<TEntity>().Attach(entity);
            _context.Entry(entity).State = System.Data.Entity.EntityState.Modified;

            var erros = _context.GetValidationErrors();
            if (erros.Count() > 0)
            {
                string stringErro = string.Empty;
                foreach (var erro in erros)
                    foreach (var er in erro.ValidationErrors)
                        stringErro += string.Format(er.ErrorMessage.ToString() + " {0}", Environment.NewLine);

                if (!string.IsNullOrEmpty(stringErro))
                    throw new Exception(stringErro);
            }
            _context.SaveChanges();
            return true;                
        }
        catch (Exception erro)
        {
            throw new Exception(erro.Message);
        }
    }

    public bool Deletar(TEntity entity)
    {
        try
        {


            _context.Set<TEntity>().Attach(entity);
            _context.Entry(entity).State = System.Data.Entity.EntityState.Deleted;

            var erros = _context.GetValidationErrors();
            if (erros.Count() > 0)
            {
                string stringErro = string.Empty;
                foreach (var erro in erros)
                    foreach (var er in erro.ValidationErrors)
                        stringErro += string.Format(er.ErrorMessage.ToString() + " {0}", Environment.NewLine);

                if (!string.IsNullOrEmpty(stringErro))
                    throw new Exception(stringErro);
            }
            _context.SaveChanges();
            return true;                
        }
        catch (Exception erro)
        {
            throw new Exception(erro.Message);
        }
    }

    public IQueryable<TEntity> GetAll()
    {
        return _context.Set<TEntity>().AsNoTracking().AsQueryable();
    }

    public IQueryable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> where)
    {
        return _context.Set<TEntity>().AsNoTracking().Where(where).AsQueryable();
    }

    public TEntity GetByHandle(long handle)
    {
        return _context.Set<TEntity>().AsNoTracking().FirstOrDefault(x => x.Handle == handle);
    }

    public IQueryable<TResult> FindSelect<TResult>(Expression<Func<TEntity, TResult>> select, Expression<Func<TEntity, bool>> where)
    {            
        return _context.Set<TEntity>().AsNoTracking().Where(where).Select(select).AsQueryable<TResult>();
    }
}

City Registration Form

public partial class FormularioCidade : FormularioBase
{
    public FormularioCidade()
    {
        InitializeComponent();            
    }

    RepositorioBase<Pais> _RepositorioPais = new RepositorioPais();
    RepositorioBase<Estado> _RepositorioEstado = new RepositorioEstado();
    RepositorioBase<Cidade> _RepositorioCidade = new RepositorioCidade();

    public override void LoadFormulario()
    {
        bsGrid.DataSource = ObterRegistrosParaGrid(x => x.Handle > 0).ToList();
    }

    public override void BotaoNovo()
    {
        bsCidade.AddNew();

        PopularBSPais();
    }

    public override void BotaoEditar()
    {
        PopularBSPais();

        PosicionarPais();
        PosicionarEstado();
    }

    private IQueryable<object> ObterRegistrosParaGrid(Expression<Func<Cidade, bool>> where)
    {
        return _RepositorioCidade.FindSelect(x => new
        {
            Handle = x.Handle,
            Descricao = x.Descricao,
            Sigla = x.Sigla,
            Estado = x.Estado,
            EstadoHandle = x.Estado.Handle,
            EstadoDescricao = x.Estado.Descricao,
            Pais = x.Estado.Pais,
            PaisHandle = x.Estado.Pais.Handle,
            PaisDescricao = x.Estado.Pais.Descricao,
            DataCadastro = x.DataCadastro,
            DataAlteracao = x.DataAlteracao
        }, where).AsQueryable();
    }

    public override void BotaoSalvar()
    {
        if (State == EntityState.Added)
        {
            Cidade currentCidade = (bsCidade.Current as Cidade);
            Pais currentPais = (bsCidade.Current as Pais);
            Estado currentEstado = (bsCidade.Current as Estado);

            Cidade cidade = new Cidade();
            cidade.Handle = _RepositorioCidade.GetNextHandle();
            cidade.Descricao = currentCidade.Descricao;
            cidade.Sigla = currentCidade.Sigla;
            cidade.Estado = currentEstado;
            cidade.EstadoHandle = currentEstado.Handle;
            cidade.Estado.Pais = currentPais;
            cidade.Estado.PaisHandle = currentPais.Handle;
            cidade.DataAlteracao = null;
            cidade.DataCadastro = DateTime.Now;

            _RepositorioCidade.Inserir(cidade);

            var newCidade = ObterRegistrosParaGrid(x => x.Handle == cidade.Handle).FirstOrDefault();

            bsGrid.Add(newCidade);


        }
        else if (State == EntityState.Modified)
        {
            Cidade currentCidade = (bsCidade.Current as Cidade);
            Pais currentPais = (bsPais.Current as Pais);
            Estado currentEstado = (bsEstado.Current as Estado);

            Cidade cidade = new Cidade();
            cidade.Handle = currentCidade.Handle;
            cidade.Descricao = currentCidade.Descricao;
            cidade.Sigla = currentCidade.Sigla;
            cidade.Estado = currentEstado;
            cidade.EstadoHandle = currentEstado.Handle;
            cidade.Estado.Pais = currentPais;
            cidade.Estado.PaisHandle = currentPais.Handle;
            cidade.DataAlteracao = DateTime.Now;
            cidade.DataCadastro = currentCidade.DataCadastro;

            _RepositorioCidade.Atualizar(cidade);


            var newCidade = ObterRegistrosParaGrid(x => x.Handle == cidade.Handle).FirstOrDefault();

            var indice = bsGrid.IndexOf(bsGrid.Current);
            bsGrid.RemoveAt(indice);
            bsGrid.Insert(indice, newCidade);
            bsGrid.Position = indice;
        }
    }

    public override void BotaoCancelar()
    {
        PopularCamposCadastro();
    }

    public override void BotaoExcluir()
    {
        Cidade cidade = (bsCidade.Current as Cidade);

        _RepositorioCidade.Deletar(cidade);

        var indice = bsGrid.IndexOf(bsGrid.Current);
        bsGrid.RemoveAt(indice);
    }

    public override void BotaoPesquisar()
    {

    }

    private void bsPais_CurrentChanged(object sender, EventArgs e)
    {
        long handlePais = (bsPais.Current as Pais).Handle;
        PopularBSEstado(handlePais);
    }

    private void bsGrid_CurrentChanged(object sender, EventArgs e)
    {
        PopularCamposCadastro();

        lblTotalRegistros.Text = string.Format("Registro {0} de {1}", bsGrid.IndexOf(bsGrid.Current) + 1, bsGrid.Count);
    }

    private void PopularCamposCadastro()
    {
        if (bsGrid.Current != null)
        {
            Cidade cidade = (bsGrid.Current as object).ToEntity<Cidade>();
            Pais pais = (bsGrid.Current as dynamic).Pais;
            Estado estado = (bsGrid.Current as dynamic).Estado;

            bsCidade.DataSource = cidade;
            bsPais.DataSource = pais;
            bsEstado.DataSource = estado;
        }
    }

    private void PopularBSPais()
    {
        bsPais.DataSource = _RepositorioPais.GetAll().ToList();
    }

    private void PopularBSEstado(long handlePais)
    {
        if (handlePais > 0)
        {
            bsEstado.DataSource = _RepositorioEstado.Find(x => x.PaisHandle == handlePais).ToList();
        }
    }

    private void PosicionarPais()
    {
        if (bsGrid.Current != null)
        {
            long handlePais = (bsGrid.Current as dynamic).PaisHandle;                
            bsPais.Position = (bsPais.IndexOf((bsPais.List as List<Pais>).FirstOrDefault(x => x.Handle == handlePais)));

        }
    }

    private void PosicionarEstado()
    {
        if (bsGrid.Current != null)
        {
            long handleEstado = (bsGrid.Current as dynamic).EstadoHandle;
            bsEstado.Position = (bsEstado.IndexOf((bsEstado.List as List<Estado>).FirstOrDefault(x => x.Handle == handleEstado)));
        }
    }

}

Class City

[Serializable]
public class Cidade : EntityBase
{
    public override long Handle { get; set; }
    public string Descricao { get; set; }
    public string Sigla { get; set; }
    public long EstadoHandle { get; set; }
    public virtual Estado Estado { get; set; }        
    public override DateTime DataCadastro { get; set; }
    public override DateTime? DataAlteracao { get; set; }
}

Fluent API City Class

public class CidadeMap : EntityBaseTypeConfiguration<Cidade>
{
    public override void ConfigureTableName()
    {
        ToTable("CIDADE");
    }

    public override void ConfigurePrimaryKey()
    {
        HasKey(x => x.Handle)
            .Property(x => x.Handle).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
    }

    public override void ConfigureForeingKeys()
    {
        HasRequired(p => p.Estado)
            .WithMany(p => p.Cidades)
            .HasForeignKey(p => p.EstadoHandle);
    }

    public override void ConfigureProperties()
    {
        Property(p => p.EstadoHandle)
            .HasColumnName("ESTADOHANDLE")
            .IsRequired();            

        Property(p => p.Descricao)
            .IsRequired()
            .HasMaxLength(150)
            .HasColumnName("DESCRICAO");

        Property(p => p.Sigla)
            .IsRequired()
            .HasMaxLength(3)
            .HasColumnName("SIGLA");
    }

    public override void ConfigureHasMany()
    {

    }
}
    
asked by anonymous 25.09.2017 / 22:33

1 answer

2

The Entity Framework context stores (caches) the entities that you have previously manipulated. When you try to insert / change an entity that already exists, it gives this error because it is already in memory. The correct is to get the entity from the context, using the Find method and update the information of this entity directly instead of trying to insert a new one.

public virtual bool Atualizar(TEntity entity)
{
    try 
    {
        if(_context.Entry(entity).State == EntityState.Detached)
        {
          // Obtém a entidade do contexto usando a chave primária da entidade que contém os dados atualizados.
          // Esta entidade está sendo rastreada pelo contexto e vai persistir as alterações feitas nela.
          var obj = _context.Set<TEntity>().Find(entity.Id);

          if (obj != null)
          {
             // Atualiza a entidade que está sendo rastreada pelo contexto com as informações contidas na entidade que você passou para este método.
             _context.Entry(entity).CurrentValues.SetValues(obj);
          }
        }

        var erros = _context.GetValidationErrors();
        if (erros.Count() > 0)
        {
            string stringErro = string.Empty;
            foreach (var erro in erros)
                foreach (var er in erro.ValidationErrors)
                    stringErro += string.Format(er.ErrorMessage.ToString() + " {0}", Environment.NewLine);

            if (!string.IsNullOrEmpty(stringErro))
                throw new Exception(stringErro);
        }
        _context.SaveChanges();
        return true;                
    }
    catch (Exception erro)
    {
        throw new Exception(erro.Message);
    }
}
    
25.09.2017 / 22:45