Error editing record more than once with Entityframework C#

Asked

Viewed 136 times

0

When editing a record and saving for the first time entityframework performs the update successfully, but when I click again on edit and save the record I come across the following error:

Error saving record: Attaching an Entity of type 'Projeto.WebERP.Entityframework.Entities.Cadastros.Localidade.Cidade' failed because Another Entity of the same type already has 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 some entities are new and have not yet Received 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?

Repositoriobase

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)));
        }
    }

}

City Class

[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()
    {

    }
}

1 answer

2


The context of the Entity Framework stores (cache) the entities you previously manipulated. When you try to enter/change an entity that already exists, it gives this error because it is in memory already. The correct is to get the entity directly the context, using the Find and update this entity’s information 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);
    }
}
  • Error remains friend.

  • Try to completely remove this line, see if the error persists.

  • which line you refer to ?

  • I updated my answer

  • worked out friend, could explain me better this way to update the record ?

  • I updated the answer with more information.

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.