Search code examples
entity-frameworkasp.net-mvc-3pocoef-code-firstscaffolding

EF 4.1 Code-First and ASP MVC 3 problem when editing entities


I coded my entities with and created DbContext. Then I used MVC Scaffolding to create simple CRUD form for one entity. So far so good, it works as advertised. Now, I decided to replace scaffolding-generated DbContext with simple Service wrapper over DbContext. All it does is delegate to DbContext.

However, now I have the problem on the following line when tried to edit the entity:

service.Entry(Book).State = EntityState.Modified;

“An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key”

I managed to resolve it like this:

PropertyInfo[] infos = typeof(Book).GetProperties();
foreach (PropertyInfo info in infos)
            {
                info.SetValue(internalBook, info.GetValue(book, null), null);
            }

Basically, I get the entity again and copy properties from entity that was handed to me by View. I have also noted that when I obtain the entity, it is proxy, and the one handed to me is not.

What could be the problem?

Here is my service class:

 public class BookService
{

    private DbContext context;
    private DbSet<Book> set;    

    public BookService(DbContext context, DbSet<Book> set) {
        this.context = context;
        this.set = set;
    }

    public IQueryable<Book> Query
    {
        get { return set; }
    }

    public virtual void Add(Book entity)
    {
        set.Add(entity);
    }        

    public virtual void Remove(Book entity)
    {
        set.Remove(entity); 
    }

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

    public List<Book> All() {
        List<Book> books = set.ToList();
        return books;
    }

    public DbEntityEntry<Book> Entry(Book book) {
        return context.Entry(book);
    }
}

Here is the Edit action Controller code. I have commented the original, scaffolding-generated code:

    [HttpPost]
    public ActionResult Edit(Book book)
    {
        Book internalBook = service.Query.Single(b => b.Id == book.Id);
        if (ModelState.IsValid)
        {
            PropertyInfo[] infos = typeof(Book).GetProperties();
            foreach (PropertyInfo info in infos)
            {
                info.SetValue(internalBook, info.GetValue(book, null), null);
            }                
            service.Entry(internalBook).State = EntityState.Modified;                
            service.SaveChanges();

            //context.Entry(book).State = EntityState.Modified;
            //context.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(book);
    }

Solution

  • You can't attach the book because you have loaded it in the same context. General approach is this:

    [HttpPost]
    public ActionResult Edit(Book book)
    {
        Book internalBook = service.Query.Single(b => b.Id == book.Id);
        if (ModelState.IsValid)
        {             
            service.Entry(internalBook).CurrentValues.SetValues(book);                
            service.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(book);
    }