首页 > 解决方案 > ASP.NET Core - 无法跟踪实体类型的实例

问题描述

尝试在编辑操作中更新上下文时遇到问题:

[HttpPost]
public IActionResult Edit(Model.LeaveApplication leaveApplication) {

ViewBag.Title = "Edit Leave Application";
ViewBag.leavePeriodList = context.LeavePeriod.ToList();
ViewBag.LeaveTypeList = context.LeaveType.ToList();

try {    
    if (ModelState.IsValid) {    
        if (!validateDateRanges(leaveApplication, "edit")) {    
            return View(leaveApplication);
        }

        leaveApplication.NumDays = (float)CalculateNoOfDays(leaveApplication);
        context.LeaveApplication.Update(leaveApplication);
        context.SaveChanges();

        return RedirectToAction("Index");
    }    
}
catch (DbUpdateException) {    
    ModelState.AddModelError("", "Unable to edit leave application. " +
    "Please try again, and if the problem persists, " +
    "please see your system administrator.");
}              
    return View(leaveApplication);
}

在此处输入图像描述

我要做的是检查是否有超过 1 条记录以验证日期范围。如果只有 1 条记录,我将跳过验证并继续更新上下文。但是,我收到一条错误消息:

无法跟踪实体类型“LeaveApplication”的实例,因为已在跟踪具有相同键的此类型的另一个实例。

是什么导致了这个问题?

这是我的构造函数代码:

public class LeaveApplicationController : Controller {

    LeaveDBContext context;
    private readonly UserManager<ApplicationUser> userManager;
    private readonly SignInManager<ApplicationUser> signInManager;

    public LeaveApplicationController(LeaveDBContext context, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) {

        this.context = context;
        this.userManager = userManager;
        this.signInManager = signInManager;
    }
}

标签: asp.net-mvcentity-frameworkasp.net-core

解决方案


永远不应该尝试保存通过帖子创建的对象。出现这种情况有许多安全原因,但您也偶然发现了一个基于 EF 工作方式的实用安全原因。

如果没有更多代码库,很难准确判断,但在某些时候,这个特定LeaveApplication实例会加载到您的上下文中,然后开始跟踪它。当您尝试保存不同 LeaveApplication的实例(即已发布的实例)时,EF 无法保存,因为它不跟踪实例,而是跟踪之前加载的实例。

您应该做的是发布到视图模型类。然后,LeaveApplication直接从上下文中提取要编辑的实例,并将视图模型中的数据映射到该实例上。最后,保存该实例。然后,您将不再有任何问题,并且您将获得防止过度黑客攻击和其他后期数据篡改的附带好处。此外,您的LeaveApplicationid 应该是 URL 的一部分。永远不要相信发布的 ID。时期。由于 URL 中的 id 唯一标识资源,因此您可以轻松地基于此实现对象级权限。


推荐阅读