实体类型“BookLoan”的实例无法被跟踪。

14

我尝试更新一个实体,但遇到以下错误:

InvalidOperationException: 实体类型 'BookLoan' 的实例无法被跟踪,因为已经有另一个具有相同关键字的实例正在被跟踪。当添加新实体时,对于大多数关键类型,如果未设置关键字(即如果将关键字属性分配为其类型的默认值),则将创建唯一的临时关键字值。如果您明确地为新实体设置关键字值,请确保它们不会与现有实体或为其他新实体生成的临时值发生冲突。在附加现有实体时,请确保只有一个具有给定关键字值的实体实例附加到上下文中。

我做了一些研究,从我的理解来看,当我使用 _context.Update(bookloan); 时,我似乎正在尝试跟踪一个已经被跟踪的实体,但我不确定该怎么做。

我想要做的是更新数据库中的现有实体/记录。这里是我的get和post控制器,因为我不确定还应该分享什么。

Get

    [HttpGet]
    public async Task<IActionResult> Return(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        if (isBookCheckedOut(id) == false)
        {
            //Not checked out
            return RedirectToAction("Index");
        }
        else
        {
            var bookloan = (from book in _context.Books.Where(b => b.BookId == id)
                        join loan in _context.BookLoans.Where(x => !x.ReturnedOn.HasValue) on book.BookId equals loan.BookID into result
                        from loanWithDefault in result.DefaultIfEmpty()
                        select new BookReturnViewModel
                        {
                            BookLoanID = loanWithDefault.BookLoanID,
                            BookID = book.BookId,
                            Title = book.Title,
                            StudentID = loanWithDefault == null ? null : loanWithDefault.StudentID,
                            StudentFristName = loanWithDefault == null ? null : loanWithDefault.Student.FirstName,
                            StudentLastName = loanWithDefault == null ? null : loanWithDefault.Student.LastName,
                            //Fines
                            CheckedOutOn = loanWithDefault == null ? (DateTime?)null : loanWithDefault.CheckedOutOn,
                            IsAvailable = loanWithDefault == null,
                            AvailableOn = loanWithDefault == null ? (DateTime?)null : loanWithDefault.DueOn
                        }).FirstOrDefault();

            if (bookloan == null)
            {
                return NotFound();
            }

            return View(bookloan);
        }
    }

发布:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Return(BookReturnViewModel model)
    {


        if (ModelState.IsValid && isBookCheckedOut(1) == true)
        {
            var bookloan = new BookLoan()
            {
                BookLoanID = model.BookLoanID,
                BookID = model.BookID,
                StudentID = model.StudentID,
                CheckedOutOn = (DateTime)model.CheckedOutOn,
                DueOn = (DateTime)model.AvailableOn,
                ReturnedOn = DateTime.Now
            };


            try
            {

                _context.Update(bookloan);
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {

            }

            return RedirectToAction("Index");
        }
        else
        {

        }

        return View();
    }

2
不要 '创建' 新的 BookLoan,根据ID从数据库获取现有实体(例如,BookLoan data = db.BookLoans.Where(x => x.BookLoanID == model.BookLoanID).FirstOrDefault();),根据视图模型更新其属性,然后保存。 - user3559349
谢谢,那个完美地解决了我的问题!我想把它标记为答案,但我不认为我可以在评论中这样做。我尝试的方法只是模仿我看到Entity在使用脚手架进行编辑时所做的。 - Christopher Johnson
1个回答

18

你的上下文已经包含了实体,所以不要创建一个新的实体,而是根据实体的ID获取现有的实体并更新其属性,然后保存它。

if (ModelState.IsValid && isBookCheckedOut(1) == true)
{
    // Get the existing entity
    BookLoan bookLoan = db.BookLoans.Where(x => x.BookLoanID == model.BookLoanID).FirstOrDefault();
    if (bookLoan != null)
    {
        bookLoan.BookID = model.BookID;
        bookLoan.StudentID = model.StudentID;
        .... // update other properties as required
        _context.Update(bookloan);
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    ....

顺便提一下:在返回视图时,最好使用return View(model);将模型传回 - 即使您不这样做(因为它们从ModelState中获取值),您的表单控件也会被正确填充,但是如果您有任何对模型属性的引用(例如<div>@Model.someProperty</div>),它会抛出异常。


2
可能更快的方法是执行 db.BookLoans.Find(model.BookLoanID),因为它会首先尝试从上下文中检索数据,然后再到数据库中查询。 - sdrevk
2
你如何在不使用大量 existingObject.Property1 = updatedObject.Property1 的代码的情况下完成它? - CarComp

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