Problems saving an already edited entity in the Entity framework core context

Asked

Viewed 243 times

1

Hello

I’m working with Asp net core my project follows DDD architecture, I’m having error with Entity Frameworks Core, when saving an altered entity, ie I load it in the frontend I change a property and I will save it at this point of saving my backend returns that there already exists another entity with the same id code in the context.

How can I resolve this error I have tried in several ways by disabling Track entities or using the property Asnotracking() but nothing worked.

It follows the error and the excerpt of the code that is giving the error, I am using Repository Generic with unitOfWork and a Context obviously from the Entity framework.

inserir a descrição da imagem aqui

Where I’m having the mistake and here!

    public void Edit(TEntity entity)
    {
        dbSet.Attach(entity);
        var entry = context.Entry(entity);
        entry.State = EntityState.Modified;
    }

This is my Generic base

public class RepositoryBase<TEntity> : IRepositoryBase<TEntity> where TEntity : class
{
    private CollegiumContext context;
    private DbSet<TEntity> dbSet;

    public RepositoryBase(CollegiumContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    public List<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
    {
        IQueryable<TEntity> query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public TEntity GetItem(int id)
    {
        return dbSet.Find(id);
    }

    public void Add(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public void Delete(int id)
    {
        TEntity entity = dbSet.Find(id);
        Delete(entity);
    }

    public void Delete(TEntity entity)
    {
        if (context.Entry(entity).State == EntityState.Detached)
        {
            dbSet.Attach(entity);
        }
        dbSet.Remove(entity);
    }

    public void Edit(TEntity entity)
    {
        dbSet.Attach(entity);
        var entry = context.Entry(entity);
        entry.State = EntityState.Modified;
    }    
}

This is my work unit that I use between the Date layers that are the Positions and the Services:

    public class UnitOfWork : IUnitOfWork, IDisposable
{
    private CollegiumContext context;

    public RepositoryBase<Acesso> AcessoRepository { get; set; }
    public RepositoryBase<Aluno> AlunoRepository { get; set; }
    public RepositoryBase<Colaborador> ColaboradorRepository { get; set; }
    public RepositoryBase<Boletim> BoletimRepository { get; set; }
    public RepositoryBase<Curso> CursoRepository { get; set; }
    public RepositoryBase<Feriado> FeriadoRepository { get; set; }
    public RepositoryBase<Instituicao> InstituicaoRepository { get; set; }
    public RepositoryBase<Lote> LoteRepository { get; set; }
    public RepositoryBase<Materia> MateriaRepository { get; set; }
    public RepositoryBase<Periodo> PeriodoRepository { get; set; }
    public RepositoryBase<Professor> ProfessorRepository { get; set; }
    public RepositoryBase<VisitaMotivo> VisitaMotivoRepository { get; set; }
    public RepositoryBase<CursoMateria> CursoMateriaRepository { get; set; }
    public RepositoryBase<AlunoCurso> AlunoCursoRepository { get; set; }
    public RepositoryBase<ProfessorMateria> ProfessorMateriaRepository { get; set; }

    public UnitOfWork(bool enableLazyLoading = false)
    {
        this.context = new CollegiumContext();
        context.ChangeTracker.AutoDetectChangesEnabled = false;
        context.ChangeTracker.LazyLoadingEnabled = enableLazyLoading;

        AcessoRepository = new RepositoryBase<Acesso>(context);
        AlunoRepository = new RepositoryBase<Aluno>(context);
        ColaboradorRepository = new RepositoryBase<Colaborador>(context);
        BoletimRepository = new RepositoryBase<Boletim>(context);
        CursoRepository = new RepositoryBase<Curso>(context);
        FeriadoRepository = new RepositoryBase<Feriado>(context);
        InstituicaoRepository = new RepositoryBase<Instituicao>(context);
        LoteRepository = new RepositoryBase<Lote>(context);
        MateriaRepository = new RepositoryBase<Materia>(context);
        PeriodoRepository = new RepositoryBase<Periodo>(context);
        ProfessorRepository = new RepositoryBase<Professor>(context);
        VisitaMotivoRepository = new RepositoryBase<VisitaMotivo>(context);
        CursoMateriaRepository = new RepositoryBase<CursoMateria>(context);
        AlunoCursoRepository = new RepositoryBase<AlunoCurso>(context);
        ProfessorMateriaRepository = new RepositoryBase<ProfessorMateria>(context);
    }

    public void Save()
    {
        context.SaveChanges();
    }

    public void SaveAsync()
    {
        context.SaveChangesAsync();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void BeginTransaction()
    {
        throw new NotImplementedException();
    }
}

I put this code in the above object and nothing changed continued the same error, did not understand how to solve now.

        this.context = new CollegiumContext();
        context.ChangeTracker.AutoDetectChangesEnabled = false;

And always in time to save my edited object. If someone went through it and want to share some solution I am grateful.

Hugs!

4 answers

0

When you give the get in your entity EF Core starts tracking it. When it returns modified it continues tracking and already knows that it is modified. In your Edit method when you attach it gets duplicated in context, so the error when you save.

0

Hello, I went through the same problem as you when starting my first DDD project using the Entity Framework Core.

Your query is returning a tracked object, just put it in the repositories in all searches dbSet.AsNoTracking().[condição de busca].

Doing so your object will not be being tracked and avoids unnecessary use of memory.

0

Hello folks I put so in the search base methods, so it applied to all entities.

    public IQueryable<TEntity> Query => _DbSet.AsQueryable().AsNoTracking();

    public TEntity Find(int id)
    {
        var result = _DbSet.Find(id);
        _Db.Entry(result).State = EntityState.Detached;

        return result;
    }

    public ICollection<TEntity> Find()
    {
        return _DbSet.AsNoTracking().ToList();
    }

    public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> predicate)
    {
        return _DbSet.AsNoTracking().Where(predicate);
    }

0

Usually whenever we search for an object in the database using Efcore it creates a trace ("tracking") of the object, so two options to solve this problem. It’s them:

a) Remove object tracking by returning it;

b) Change the tracked object and save the change through Savechanges, so you don’t need to send the object to any other method to treat it.

Browser other questions tagged

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