Update specific fields in Entity Framework

c# entity-framework

Question

I have a model:

public class VR
{
    [Key]
    public int ID { get; set; }
    public string FullName { get; set; }
    public string CreatedBy { get; set; }
    public DateTime? Created { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime? Modified { get; set; }
}

My controller edit function:

    // POST: VRs/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Edit(VR vR)
    {
        if (ModelState.IsValid)
        {
            var Result = (from c in _context.MyVR.Where(c => c.ID == vR.ID) select c).Single();

            vR.Created = Result.Created;
            vR.CreatedBy = Result.CreatedBy;
            vR.ModifiedBy = User.Identity.Name;
            vR.Modified = DateTime.Now;

            _context.Update(vR);
            _context.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(vR);
    }

and I get below error:

The instance of entity type 'UNTest.ViewModels.VR' cannot be tracked because another instance of this type with the same key is already being tracked. For new entities consider using an IIdentityGenerator to generate unique key values.

1
5
3/13/2016 7:21:18 AM

Accepted Answer

You can Attach the entity to avoid loading it from DB (save performance) and update only the fields you want.

This also avoids the problem of your code when you load an instance from the DB (Result) and track another instance with the same Id (vR) resulting in an exception.

// POST: VRs/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Edit(VR vR)
    {
        if (ModelState.IsValid)
        {
            //Attach the instance so that we don't need to load it from the DB
            _context.MyVR.Attach(vR);

            vR.ModifiedBy = User.Identity.Name;
            vR.Modified = DateTime.Now;

            //Specify the fields that should be updated.
            _context.Entry(vR).Property(x => x.ModifiedBy).IsModified = true;
            _context.Entry(vR).Property(x => x.Modified).IsModified = true;

            _context.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(vR);
    }

The other way to specify fields that should not be updated.

// POST: VRs/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(VR vR)
        {
            if (ModelState.IsValid)
            {
                //Attach the instance so that we don't need to load it from the DB
                _context.Entry(vR).State = EntityState.Modified; 

                vR.ModifiedBy = User.Identity.Name;
                vR.Modified = DateTime.Now;

                //Specify the fields that should not be updated.
                _context.Entry(vR).Property(x => x.Created).IsModified = false;
                _context.Entry(vR).Property(x => x.CreatedBy).IsModified = false;

                _context.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(vR);
        }

In case you use a view model, you can use new operator to create your data model and copy the fields you want to update:

// POST: VRs/Edit/5
            [HttpPost]
            [ValidateAntiForgeryToken]
            public IActionResult Edit(VRViewModel vRVM)
            {
                if (ModelState.IsValid)
                {
                    VR vR = new VR();
                    //Attach the instance so that we don't need to load it from the DB
                    _context.MyVR.Attach(vR);

                    //Set the Id for your model.
                    vR.Id = vRVM.Id;
                    //Let's say you also want to update this field from the VM
                    vR.FullName = vRVM.FullName;

                    vR.ModifiedBy = User.Identity.Name;
                    vR.Modified = DateTime.Now;

                    //Specify the fields that should be updated.
                    _context.Entry(vR).Property(x => x.ModifiedBy).IsModified = true;
                    _context.Entry(vR).Property(x => x.Modified).IsModified = true;
                    _context.Entry(vR).Property(x => x.FullName).IsModified = true;

                    _context.SaveChanges();
                    return RedirectToAction("Index");
                }
                //create your new view model and return it. For demonstration purpose, I return the same view model, in your real code, you can adjust it.
                return View(vRVM);
            }
10
3/13/2016 7:47:54 AM

Popular Answer

You have retrieved the data entity from the context so its being tracked. You cannot then track another entity with the same ID in the same context.

Change the POST method to update the data model from the view model, and save the data model

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(VR vR)
{
    if (ModelState.IsValid)
    {
        var result = (from c in _context.MyVR.Where(c => c.ID == vR.ID) select c)
           .FirstOrDefault(); // use FirstOrDefault() to prevent an exception if the user changes you input for the ID.
        if (result != null)
        {
           result.FullName = vR.FullName;
           result.ModifiedBy = User.Identity.Name;
           result.Modified = DateTime.Now;
           _context.Update(result);
           _context.SaveChanges();
            return RedirectToAction("Index");
        }
    return View(vR);
}


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow