Leon Cullens
Leon Cullens

Reputation: 12476

Entities lose change-tracking when edited with MVC3

I built a very simple MVC3 application to do a little demo, but I'm running against a problem; I return an entity to my view, edit it and then post it back, but in this process my entity loses it's change-tracking capabilities. When I return the entity to my view it's still an entity framework proxy class, but when it comes back from my view it's a 'Person' class (the entity is called person).

Here's my repository class:

public class PersonRepository : IPersonRepository
{
    public EfContext Uow { get; set; }

    public PersonRepository(IUnitOfWork uow)
    {
        Uow = uow as EfContext;
    }

    // yada yada yada

    public void Add(Person person)
    {
        Uow.Persons.Add(person);
    }
}

This entity is sent to my view that has a simple form, created with Html.EditorForModel. After that I post it back to this method:

[HttpPost]
public ActionResult Edit(Person person)
{
    if (ModelState.IsValid)
    {
        _personRepository.Add(person);
        _personRepository.Uow.Commit();

        return RedirectToAction("Index");
    }

    return View(person);
}

And tada, it's no longer a tracked proxy class. This results in a primary key violation because entity framework is trying to add my object as a new object, where I just want entity framework to detect the changes and create an update statement instead. Oh by the way, the Commit method in the code above just calls SaveChanges(), here's the class:

public class EfContext : DbContext, IUnitOfWork
{
    public DbSet<Account> Accounts { get; set; }
    public DbSet<Person> Persons { get; set; } 

    public void Commit()
    {
        SaveChanges();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

By the way, this is my entity class:

public class Person
{
    [HiddenInput(DisplayValue = false)]
    public Guid Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public virtual ICollection<Account> Accounts { get; set; } 
}

Does anyone know how to fix this? I had this working before as far as I can remember, I just don't know how.

Thanks in advance!

Upvotes: 1

Views: 410

Answers (3)

Sebazzz
Sebazzz

Reputation: 1373

There is a 3rd solution here, which I believe is the most failsafe one.

You can configure your MVC action to receive an id integer. Then, using that integer, you retrieve the entity from the database. Then you call the UpdateModel method on the controller. This will bind the new form values to your existing object while it doesn't change the unchanged properties and properties that may not have been in the form.

Upvotes: 0

Jaime
Jaime

Reputation: 6814

I assume that you're creating your repository per request (in the constructor or passed in as a dependency by your IoC framework or something). In that case, the entity that you receive in your controller method is indeed not tracked, because it was created in a different request. You have to "attach" it to the context and mark it as modified so EF knows it has changed and it needs to be saved to the DB

Upvotes: 2

Eranga
Eranga

Reputation: 32437

Define an Update method in your repository that will attach the entity and mark it as modified.

public class PersonRepository : IPersonRepository
{
    // yada yada yada

    public void Add(Person person)
    {
        Uow.Persons.Add(person);
    }

    public void Update(Person person)
    {
        Uow.Persons.Attach(person);
        Uow.Entry(person).State = EntityState.Modified;
    }
}

[HttpPost]
public ActionResult Edit(Person person)
{
    if (ModelState.IsValid)
    {
        _personRepository.Update(person);
        _personRepository.Uow.Commit();

        return RedirectToAction("Index");
    }

    return View(person);
}

Upvotes: 1

Related Questions