在Entity Framework中更新特定字段

7

我有一个模型:

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

我的控制器的Edit函数:

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

我遇到了以下错误:

由于已经正在跟踪具有相同键的此类型的另一个实例,因此无法跟踪实体类型 'UNTest.ViewModels.VR' 的实例。对于新实体,请考虑使用 IIdentityGenerator 来生成唯一的键值。


1
你的方法不正确 - 使用 Result.someProperty = vr.someProperty; 等等,然后使用 .Update(Result); - user3559349
我希望当有人更新此页面时,创建该项的人和创建日期不应更改,我只希望修改人和修改日期发生变化。你能给出详细的例子吗? - MJ X
这个回答解决了你的问题吗?如何使用Entity Framework和EntityState.Modified更新对象的非所有字段 - Michael Freidgeim
2个回答

20
您可以使用Attach方法将实体附加到对象上,以避免从数据库加载它(提高性能),并仅更新所需字段。
这也避免了当您从数据库加载一个实例(Result)并跟踪另一个具有相同Id的实例(vR)时,代码出现异常的问题。
// 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);
    }

另一种指定不应更新的字段的方法。
// 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);
        }

如果您使用视图模型,可以使用“new”运算符创建数据模型并复制要更新的字段:
// 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);
            }

1
@Mehdi Jalal:尝试移除“_context.Update(vR)” - Khanh TO
我已经移除了,但它没有更新我的数据库其他字段,是否应该像您为ModifiedBy和Modified所做的那样重新输入每个字段? - MJ X
谢谢,这很完美 :) - MJ X
@Stephen Muecke:那只是错误,很容易纠正。我现在会进行更正。重要的是方法。 - Khanh TO
@Stephen Muecke:代码已更新。我们不需要设置“Created”和“CreatedBy”,因为我们不想更新这些字段。 - Khanh TO
显示剩余10条评论

4
您已经从上下文中检索到数据实体,因此该实体正在被跟踪。您不能在同一上下文中再跟踪具有相同ID的另一个实体。
将POST方法更改为从视图模型更新数据模型,并保存数据模型。
[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);
}

@Mehdi Jalal:这并不是毫无意义的。首先,我不需要从数据库中加载实体,我可以使用new运算符创建一个新的VR对象并将其附加到上下文中(即使我的视图模型不是VR)。其次,视图模型可能包含其他与模型无关但仅与视图相关的属性(这就是为什么它被命名为视图模型),我们真的不想保存与视图相关的属性。最终我们必须使用模型来工作。 - Khanh TO
@Mehdi Jalal:只更新我们想要的字段而不从数据库加载实体是“更好的”。首先,有性能优势。其次,当我们更新从DB加载的其他字段时,我们不会遇到并发问题。当这些不需要的字段被加载和更新时,我们认为我们使用从db加载的相同值进行更新,但实际上它可能会覆盖其他用户在这些字段上的更新,如果同时,其他线程正在更新其他字段。 - Khanh TO
@Mehdi Jalal:这段代码存在一个并发问题。假设你的VR对象还有其他unchanged字段(例如FirstName)。当实体被加载时,FirstName="A",但是在此期间,另一个线程可能会发生更新并将FirstName更新为"B"。当你保存更改时,你会意外覆盖该值,并将其设置回"A"。此外,更新这些字段也会不必要地降低性能。 - Khanh TO
@KhanhTO,我猜你不是用asp.net.mvc开发的。当你创建编辑对象的视图时,你使用视图模型(而不是数据模型),并将视图模型提交回服务器。然后你获取数据模型,根据视图模型更新其属性,并保存数据模型。如果你在视图中采用了数据模型这种不好的做法,那么你的解决方案就会很好地工作。 - user3559349
@Stephen Muecke:使用视图模型没有问题,我们只需要使用“new”创建数据模型并从视图模型复制字段,然后附加数据模型并保存模型即可。这只是一个小细节,主要是“如果我们想要更新数据库,我们无论如何都必须使用数据模型”。请记住,视图模型可能包含来自多个模型的信息(请阅读您发布的链接),因此我们无论如何都必须从视图模型中复制信息。 - Khanh TO
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接