.Update() returns "Cannot update identity column"

.Update() returns "Cannot update identity column"

在索引型显示器上有一个 link 将控制指向下面的方法。凭证状态更新为 "Reconciled" 然后尝试保存。

一切似乎都按预期执行,直到 Update/Save 当我得到异常时,"Cannot update identity column."

Voucher 模型中没有导航属性。两个标识列都在插入时很好地填充。

模特在这里:

public class Voucher
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public string VoucherId { get; set; }

        [Required]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Int64 VoucherNumber { get; set; }
        [Required]
        public string StudentId { get; set; }

        public string FullName { get; set; }

        [Required]
        public string VoucherType { get; set; }

        [Required]
        public DateTime VoucherCreateDate { get; set; }

        [Required]

        public string VoucherStatus { get; set; }
}

public ViewResult ReconcileVoucher(string id) //, Voucher voucher)
        {
            Voucher voucher = _context.Vouchers
                .Single(m => m.VoucherId == id);

            if (id != voucher.VoucherId)
            {
                ModelState.AddModelError("InputStatus", "Indicated Voucher is not present " + voucher.FullName);
                return View();
            }

            voucher.VoucherStatus = "Reconciled";

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(voucher);
                    _context.SaveChanges();
                    ModelState.AddModelError("InputStatus", "Voucher was Reconciled for " + voucher.FullName);
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    CreateLogRow("Reconcile Voucher", "testUser", "Concurrency exception for " + voucher.StudentId, ex.Message);
                    if (!VoucherExists(voucher.VoucherId))
                    {
                        ModelState.AddModelError("InputStatus", "The Voucher for " + voucher.FullName + " cannot be found");
                        return View();
                    }
                    else
                    {
                        throw;
                    }
                }
                CreateLogRow("Reconcile Voucher", "testUser", "Voucher was edited for " + voucher.StudentId + " -  " + voucher.FullName, null);
                return View();
            }
            return View(voucher);
        }

您通过调用

将所有属性标记为已修改
 _context.Update(voucher);

要么省略此调用并让更改跟踪器确定更新的列,要么明确将 VoucherNumber 属性 标记为未修改。

db.Update(voucher);
db.Entry(voucher).Property(nameof(Voucher.VoucherNumber)).IsModified = false;

当您在调用中从 DbContext 获取实体并更新 属性 时,您不应调用 Update,而应仅调用 SaveChanges.

Voucher voucher = _context.Vouchers
    .Single(m => m.VoucherId == id);

// ...
voucher.VoucherStatus = "Reconciled";
// ...
_context.SaveChanges();

Update 将生成一个语句,该语句将更新实体上的 all 列,虽然这应该排除标记为 DatabaseGeneratedOption.Identity 的列,但这可能这里不是这种情况。通过让更改跟踪在没有 Update 调用的情况下执行其操作,EF 将仅为更改的列生成 UPDATE 语句。

Update 在接受一个实体(如您注释掉的那样)和 attaching/updating 其整体状态时会更适用。我不推荐这种方法,因为它会覆盖所有值并且容易受到许多问题的影响,包括陈旧的数据更新和来自客户端的意外篡改。 (通过 man-in-the-browser/middle 攻击修改您的 UI 不允许的列)最好加载新的实体,验证传入数据,检查行 versions/modified 时间戳是否有陈旧更新等,而不是接受一个面值的实体并将其推入数据库。

编辑:如果这些 ID 在数据库中是 GUID,那么为什么不将它们转换为实体中的 GUID?