关于
SaveChanges
上的 PK 违规:这些通常是由以下两种情况之一引起的:PK 未设置为作为身份运行且未在代码中指定唯一 ID,或者尝试使用以下方式将引用关联到现有行:这些相关实体的未追踪副本。
身份PK
默认情况下,大多数数据库提供程序和 EF 会将名为“Id”或“{EntityName}Id”/“{EntityName}_Id”(即 MyObjectId)的
int
/long
属性识别为将由数据库填充的标识列保存。但是,某些数据库(例如内存数据库)不提供此功能,因此身份行为将被忽略。当尝试插入 ID 为 0 的行(其中已存在具有该 ID 的行)时,这可能会导致错误。如果您使用非常规 PK 名称(例如“MyObjectKey”),那么您需要将其标识为 PK 和标识列。由于您的数据库名为“Id”,只要您使用支持标识列的数据库,就不应该出现这种情况。
未跟踪的参考文献
这将涉及创建一个新的 MyObject,该对象引用另一个现有实体。例如,如果 MyObject 引用了 Status,它是指向 Statuses 查找表的实体,并且您执行了类似的操作:
using var dbcontext = DbFactory.CreateDbContext();
MyObject c = new MyObject
{
MyMember1="abc",
MyMember2="123",
Status = new Status { StatusId == 1 } // "New" status
};
dbcontext.MyObjects.Add(c);
await dbcontext.SaveChangesAsync();
这会导致 PK 错误。虽然您可能只想插入 MyObject 并引用“New”/w ID 1 的现有状态,但 DbContext 不会跟踪该状态,因此它将被解释为新状态。这包括您可能将状态“实体”传递到此方法的情况,例如从 Web 客户端返回的引用。
相反,您希望确保对数据库中现有记录的任何引用首先由 DbContext 加载和跟踪。这确保它知道将新实体关联到现有引用,而不是尝试插入新引用:
var status = dbContext.Statuses.Single(x => x.StatusId == 1); // "New status"
MyObject c = new MyObject
{
MyMember1="abc",
MyMember2="123",
Status = status
};