7

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's 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 the error below:

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.

3
  • 1
    You have it the wrong way around - use Result.someProperty = vr.someProperty; etc and .Update(Result); Commented Mar 13, 2016 at 4:43
  • I want that when someone update this page the person who created the Item and the created date should not change, I want only the Modified person and Modified date change can you give a detail example Commented Mar 13, 2016 at 4:49
  • Does this answer your question? How to update not every fields of an object using Entity Framework and EntityState.Modified Commented Jan 29, 2023 at 11:53

2 Answers 2

20

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);
            }
Sign up to request clarification or add additional context in comments.

15 Comments

When I do this it makes the Created field and CreatedBy field null
@Mehdi Jalal: Try removing _context.Update(vR)
This is also more efficient as it updates only the fields we want instead of updating all the fields which can also run into concurrency problems if other threads also update the other fields.
@Mehdi Jalal: Did you try removing _context.Update(vR);?
I did removed but it does not update my database other fields, should I retype each field the same as you did for ModifiedBy and Modified
|
4

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);
}

7 Comments

Once you get serious about using MVC and start using view models you will realize that the code in the other answer is going to be pointless :)
@Mehdi Jalal: It's not pointless. First, I don't need to load the entity from the DB, I can use new operator to create a new VR object and attach it to the context (even my view model is not VR). Second, View Model may contain other properties not related to model, but to view only (that's why it's named view model), we really don't want to save properties related to view. We have to work with models eventually.
@Mehdi Jalal: It's much better to update only the fields we want without loading the entity from the db. First, there is a performance advantage. Second, we don't run into concurrency problem when we update the other fields loaded from DB. When these unwanted fields are loaded and updated, we think that we update with the same value loaded from db, but it's not, it could override updates from other users on those fields if at the same time, the other fields are being updated on other threads.
@Mehdi Jalal: This code has a concurrency problem. Let's say, your VR object has another unchanged fields (e.x FirstName). When the entity is loaded, the FirstName = "A", but during that time, another update could happen in between on another thread and update the FirstName = "B". When you SaveChanges, you accidentally override the value and set it back to "A". Furthermore, updating those fields also slows down performance unnecessarily.
@KhanhTO, I assume you do not develop in asp.net.mvc. When you create views for editing objects, you use a view model (not the the data model) and you post back the view model. You then get the data model, update its properties based on the view model and save the data model. Your solution works fine only if you adopt the bad practice of using a data model in the view.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.