schneck
schneck

Reputation: 10827

Update on Entity fails using generic Repository

I have a generic repository:

    public class GenericRepository<TEntity> : AbstractRepository<TEntity>, IRepository<TEntity> where TEntity : class
    {
        private DbContext _context;
        [...]
        public GenericRepository(DbContext context)
        {
            _context = context;
            context.Configuration.AutoDetectChangesEnabled = true;
            _dbSet = _context.Set<TEntity>();
        }
        [...]
        public void SaveChanges()
        {
            _context.SaveChanges();
        }
        [...]
    public void Add(TEntity entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        _dbSet.Add(entity);
    }
        [...]
    public virtual void Update(TEntity entity)
    {
        _context.Entry(entity).State = EntityState.Modified;
    }

In my controller, there is this code:

    [HttpPost]
    public ActionResult Edit(Project project)
    {
          if (ModelState.IsValid)
        {
            if (project.Id == 0)
            {
                ProjectRepository.Add(project);
            }
            else
            {
                ProjectRepository.Update(project);
            }

            ProjectRepository.SaveChanges();
            [...]

Selecting and Inserting works fine, but Updating not: I get an InvalidOperationException (english translation of the german error message is "An object with the same key already exists in the Object State Manager. The Object State Manager can not track multiple objects with the same key.").

I don't understand that, because I'm the only user on my development machine, and I did not modify the record at another place.

Any idea what I'm doing wrong here?

Upvotes: 2

Views: 7448

Answers (4)

FLICKER
FLICKER

Reputation: 6683

This is my solution for this problem in EF Core. Not sure if it can be done on EF6.

Repository Interface

public interface IRepository<TEntity> where TEntity : class
{
    void Update(TEntity entity);

    // I ommited the rest of the methos for simplicity
}

Repository Implementation

public class GenericRepository<T> : IRepository<T> where T : class
{
    public void Update(T entity)
    {
        dbContext.Set<T>().Attach(entity);
        dbContext.Entry(entity).State = EntityState.Modified;

        // don't forget to dbContext.SaveChanges() here if needed
    }
}

Upvotes: 0

marc.d
marc.d

Reputation: 3844

Disclaimer: i never used Entity Framework, i am writing this based on my experience with ASP.NET MVC and nHibernate but you should be able to apply the same pattern

Ok first off your actual problem is in fact a double key insert, this is the case because the project-object which get passed to your edit action is not the same object you are trying to update (this one has been created from your modelbinder based on the values in the FormValueProvider). it may has the excat same values and therefor the same id but for your ORM it`s a brand new object which has never been persisted to the DB.

you can prevent this by using the following pattern (quick'n dirty sample-code)

    [HttpPost]
    public ActionResult Edit(Project project)
    {
        if (ModelState.IsValid)
        {
            if (project.Id == 0)
            {
                ProjectRepository.Add(project);
            }
            else
            {
                Project dbProject = ProjectRepository.GetById(project.Id); //or whatever your method to retrieve entities by Id is named
                UpdateModel(dbProject, "project"); //http://msdn.microsoft.com/en-us/library/dd470933.aspx
                ProjectRepository.Update(project);
            }
        }
    }

Upvotes: 0

Steen T&#248;ttrup
Steen T&#248;ttrup

Reputation: 3835

The project instance is created by model binding, not loaded from your repository, so you need to actually load a project instance from the repository, and then change the properties on that.

Upvotes: 0

Garrett Vlieger
Garrett Vlieger

Reputation: 9494

Take a look at these answers:

Basically you need to do this:

 var entity = _context.Projects.Find(project.ProjectId);
 _context.Entry(entity).CurrentValues.SetValues(project);

Hopefully this helps.

Upvotes: 8

Related Questions