2

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?

schneck
  • 10,556
  • 11
  • 49
  • 74

4 Answers4

8

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.

Community
  • 1
  • 1
Garrett Vlieger
  • 9,354
  • 4
  • 32
  • 44
  • 1
    I would like to try, but since it's generic repository, I cannot use "_context.Projects" without overwritingthe generic method. How can I find the DbSet to use in my save method? – schneck Jul 25 '12 at 11:35
  • So why not override the generic method? It's virtual so it shouldn't be a problem. – Garrett Vlieger Jul 25 '12 at 11:54
  • Yes, but this will be redundant code for all repositories, and I want to avoid this. – schneck Jul 25 '12 at 11:57
  • Well, there does not seem to be an easy, reliable and redundancy-avoiding solution, so I override the update method per repository now... Thanks anyway. – schneck Jul 25 '12 at 12:08
0

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.

Steen Tøttrup
  • 3,755
  • 2
  • 22
  • 36
  • I would also reconsider using database models as view models, I think if often (most of the time) makes sense to keep these as 2 separate classes. – Steen Tøttrup Jul 25 '12 at 11:13
0

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);
            }
        }
    }
marc.d
  • 3,804
  • 5
  • 31
  • 46
  • Yes, I did it in a similar way (see the accepted answer), but placed the code you have in the controller into the repository. – schneck Jul 25 '12 at 13:27
0

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
    }
}
FLICKER
  • 6,439
  • 4
  • 45
  • 75