7

Updating an object with MVC3

I have a model that I can modify, please see the sample below:

[HttpPost]
public ActionResult Edit(Company c)
{
       if (ModelState.IsValid)
       {
           db.Entry(c).State = EntityState.Modified;
           db.SaveChanges();
           return RedirectToAction("Index");
       }
       return View(c);
}

The model has other fields that are not showing in the view and cannot be modified by the user, but when I click the submit button the fields that were not showing in the view got set to null.

Can I somehow let EF know not to modify certain fields? Thanks.

Ciarán Bruen
  • 5,221
  • 13
  • 59
  • 69
Benk
  • 1,284
  • 6
  • 33
  • 64

3 Answers3

12

Generally it is better not to bind to the entity object directly, rather create an edit model and bind to that.

After all.. whats to stop someone posting back values you don't want changed with this approach?

The main problem here is the fact that mvc model binding changes the properties in the model before its in a context therefore the entity framework doesn't know which values have changed (and hence which should be updated)

You've mitigated that slightly with db.Entry(c).State = EntityState.Modified; but that tells the entity framework that the whole record has been updated.

I would normally do the following:

  1. Bind to a model specifically for this controller first
  2. Create an instance of the entity class you want to update, set the Id accordingly and attach it to the context
  3. Update the properties on the entity to be the same as the model you binded to (object is attached and therefore entity framework is tracking which columns are being changed now)
  4. SaveChanges

Step 3 is a bit tedious therefore consider using a tool like automapper to make things easier

Edit:

    [HttpPost]
    public ActionResult Edit(Company c)
    {
        if (ModelState.IsValid)
        {
            Company dbCompayObjct = new Company { companyId = c.companyId };
            db.Company.Attach(dbCompayObjct);

            dbCompanyObjct.CompanyName = c.CompanyName;
            dbCompanyObjct.City = c.City;

            db.SaveChanges();

            return RedirectToAction("Index");
        } 
        return View(c);
    }
Martin Booth
  • 8,485
  • 31
  • 31
  • Hi Martin, Thanks for your response, I modify the Edit Action to the below code, could you please let me know if this is what you meant. srry just started learning EF and MVC [HttpPost] public ActionResult Edit(Company c){ if (ModelState.IsValid) { Company dbCompanyObjct = db.Company.Find(c.companyID); dbCompanyObjct.CompanyName = c.CompanyName; dbCompanyObjct.City = c.City; db.SaveChanges(); return RedirectToAction("Index"); } return View(c); } Also, could you explain me what is automapper? how do I use it? thanks – Benk Dec 06 '11 at 00:28
  • I apologies for the code not been aligned, hard to read, no idea why it is not aligning...plz advice if the code correct. – Benk Dec 06 '11 at 01:14
  • That will work, I'll post a quick update though to show that you don't need the first Find – Martin Booth Dec 06 '11 at 01:28
  • Can TryUpdateModel not be used here? – Monstieur Mar 01 '13 at 10:23
3

You are apparently overwriting your existing record with an incomplete record. When you use the method above, it will completely replace the existing one.

You either need to fill in all the fields you don't want to replace with the existing values, or you need to get the existing record and modify the fields you want to modify, then save it.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • There is no way to somehow to specify in the model class for example field "applicationDate" to not allow to modify the value, similar how we cannot modify the primary key of the model entity? – Benk Dec 06 '11 at 00:00
  • 2
    @user1042528 - you're not understanding. You're replacing the entire record, not updating individual fields. It doesn't matter if you can prevent fields from being written to, since you're not actually writing over them. You're replacing the entire record. It's like the difference between replacing one bottle of beer in a six-pack, and just getting a different 6-pack that has only one bottle in it. You're not replacing the other 5 with nothing, you're replacing the entire record. – Erik Funkenbusch Dec 06 '11 at 00:18
  • Thx for your explanation , is this how you update individual fields? [HttpPost] public ActionResult Edit(Company c){ if (ModelState.IsValid) { Company dbCompanyObjct = db.Company.Find(c.companyID); dbCompanyObjct.CompanyName = c.CompanyName; dbCompanyObjct.City = c.City; db.SaveChanges(); return RedirectToAction("Index"); } return View(c); } – Benk Dec 06 '11 at 00:39
  • I apologies for the code not been aligned, hard to read, no idea why it is not aligning...plz advice if the code correct. – Benk Dec 06 '11 at 01:13
  • Entity framework does not always write the entire row.. it will track changes to columns and only update the ones which have changed – Martin Booth Dec 06 '11 at 01:36
  • Hi Martin, plz show an example to do that? How EF will track the changes and only update the fields which have changed? thank you! – Benk Dec 06 '11 at 01:45
  • @MartinBooth - Yes, but not in the way the author is doing it. Which is the point. – Erik Funkenbusch Dec 06 '11 at 02:06
0

Reflection is not always evil, sometimes it's your friend:

public ActionResult Edit(Company c)
{
   if (ModelState.IsValid)
   {
        Company UpdateC = db.Company.find(c.CompanyID);        
        foreach (var property in typeof(Company).GetProperties())
        {
            var propval = property.GetValue(c);
            if (propval != null)
            {
                property.SetValue(UpdateC, propval);
            }
        }

        db.SaveChanges();  
        return RedirectToAction("Index");
   }
   return View(c);
}