当 EF 无法为您的实体解析 PK 时,会发生此错误。在大多数情况下,对于简单实体,EF 约定可以计算出 PK,但在您的情况下,您使用的是复合键,因此需要对其进行配置。根据您映射实体的方式,您可以通过以下方式执行此操作:
- EDMX
- 在 DbContext.OnModelCreating 中
- 使用
EntityTypeConfiguration 声明
- 在实体本身中使用属性
由于我们不知道您的实体是如何配置的,您可以通过在您的实体中使用属性方法作为测试来验证这一点。如果您使用的是 EDMX,则会生成实体类,因此您需要将其替换为 EDMX 中的配置。 (不能真正帮助你,因为我不使用该死的东西:D)
你可能会有类似的东西:
public class CarRentalFields
{
[Column("start_day")]
public DateTime StartDay { get; set; }
[Column("return_date")]
public DateTime ReturnDate { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("car_number")]
public DateTime CarNumber { get; set; }
// ... more columns...
}
您甚至可以在这些字段之一(例如 CarNumber)上具有 [Key] 属性。如果实体中映射了 PK,则问题是它不够具体,无法唯一标识该行。当 EF 去更新一个实体时,它正在检查并期望只更新表中的一行。它发现不止一行会受到影响,所以它会失败。
将[Key] 的属性附加到列顺序中,以便将其识别为复合键。
public class CarRentalFields
{
[Key, Column(Name="start_day", Order=1)]
public DateTime StartDay { get; set; }
[Key, Column(Name="return_date", Order=2)]
public DateTime ReturnDate { get; set; }
[Key, Column(Name="user_id", Order=3)]
public int UserId { get; set; }
[Key, Column(Name="car_number", Order=4)]
public DateTime CarNumber { get; set; }
// ... more columns...
}
只要保证这 4 列是表的唯一约束,EF 在构建它的 UPDATE SQL 语句时只更新一行时将被满足。
再次注意,如果这可行并且您使用的是 EDMX,则需要检查和修改您的 EDMX 映射以进行适当的更改,因为该实体类可能会重新生成,从而丢失您的额外属性。 (我相信从 EDMX 生成的实体类有一个注释标题警告你它是一个生成的类,所以这是一个需要注意的指标。)
更新:
我的主要怀疑是该表实际上没有定义匹配的 PK,或者运行不同的 PK 组合,或者考虑到这些字段的性质,更可能没有 PK。 EF 可以对没有定义 PK 的表进行操作,但它确实需要一个确保可以唯一标识记录的 Key 定义。您看到的错误发生在该键定义不够唯一时。 (即,如果您正在更新汽车 1,并选择具有以下内容的行:
car_number = 1, start_day = 2021-11-21, return_day = 2021-11-22, user_id = 0 问题是数据库中有不止一行具有该组合。如果您正在检查的数据库没有超过一个匹配行,那么您的应用程序几乎可以肯定指向与您正在检查的不同的数据库。
您可以做的事情来验证这一点:
- 获取运行时连接字符串并查看它是否与您正在检查的数据库匹配:
在运行查询之前,添加以下内容:
// EF6
var connectionString = db.Database.Connection.ConnectionString;
// EF Core 5
var connectionString = db.Database.GetConnectionString();
- 查看您实际查询的数据:
.
var cars = db.CarRentalFields.Where(carNum =>
(carNum.CarNumber == carNumber1 && carNum.ActualReturnDate == null)).ToList();
虽然此查询可能仅返回 1 条记录,但这不是问题的原因。您需要的是该记录的 CarNumber、StartDate、ReturnDate 和 UserId:
var car = db.CarRentalFields
.Where(carNum => carNum.CarNumber == carNumber1
&& carNum.ActualReturnDate == null)
.Select(x => new
{
x.CarNumber,
x.StartDay,
x.ReturnDate,
x.UserId
}).Single(); // Expect our 1 record here...
var cars = db.CarRentalFields
.Where(x => x.CarNumber == car.CarNumber
&& x.StartDay == car.StartDay
&& x.ReturnDate == car.ReturnDate
&& x.UserId == car.UserId)
.ToList(); // Get rows that match our returned Key fields.
这些查询为您要更新的汽车记录选择假定的 PK 值,然后搜索汽车以查找具有预期键字段的匹配记录。我的钱是在顶部查询返回 1 条记录时,底部查询返回两行,这意味着虽然只有 1 条记录具有 #null ActualReturnDate 值,但您的 Key 对于该表的内容来说不够唯一。