EF Core执行返回非唯一记录的存储过程时重复键错误解决咨询
解决EF Core调用返回非唯一记录的存储过程时的"An Item with the same key has already been added"错误
问题根源
EF Core的实体类型(无论是否启用跟踪)要求键字段(或复合键组合)必须唯一,因为实体模型依赖键来标识记录。即使添加了AsNoTracking(),EF在映射存储过程返回结果时,仍会校验键的唯一性,因此当存储过程返回重复键的记录时,就会触发该错误。
解决方案
方案1:使用无键实体(推荐)
将CustomModel配置为无键实体,EF Core就不会校验记录的键唯一性,可直接读取重复记录。
- 修改DbContext中的模型配置,移除所有键配置,添加
HasNoKey():
modelBuilder.Entity<CustomModel>(entity => { // 移除原复合键配置,改为无键 entity.HasNoKey(); // 保留原有属性配置 entity.Property(e => e.ReglerObjekt).IsRequired(false); // 其他属性配置... });
- 调用存储过程的代码无需修改(
AsNoTracking()可保留,但非必须):
Dim locTableContent = dbContext.CustomModel.FromSqlRaw("EXEC sp_xyz @Id, @globalId", New SqlParameter("@Id", obj.Id), New SqlParameter("@globalId", CType(globalId, Integer?))).AsNoTracking().ToList()
注意:如果之前尝试过
HasNoKey()但未生效,大概率是没有完全移除原有的HasKey()配置,需确保模型配置中仅保留HasNoKey()。
方案2:直接使用ADO.NET读取数据
若不想修改实体配置,可绕过EF Core的实体映射,直接用原生ADO.NET执行存储过程并手动映射结果:
Dim locTableContent As New List(Of CustomModel)() Using connection = dbContext.Database.GetDbConnection() connection.Open() Using command = connection.CreateCommand() command.CommandText = "EXEC sp_xyz @Id, @globalId" ' 添加参数 command.Parameters.Add(New SqlParameter("@Id", obj.Id)) command.Parameters.Add(New SqlParameter("@globalId", CType(globalId, Integer?))) Using reader = command.ExecuteReader() While reader.Read() Dim model As New CustomModel() ' 手动映射字段到模型属性 model.ReglerObjekt = If(reader.IsDBNull(reader.GetOrdinal("ReglerObjekt")), Nothing, reader.GetInt32(reader.GetOrdinal("ReglerObjekt"))) model.Typ = If(reader.IsDBNull(reader.GetOrdinal("Typ")), Nothing, reader.GetString(reader.GetOrdinal("Typ"))) model.StatusNr = reader.GetInt32(reader.GetOrdinal("StatusNr")) ' 映射其他属性... locTableContent.Add(model) End While End Using End Using End Using
对之前尝试方案的补充说明
- 复合键:若存储过程返回的记录中,
Id+Typ+StatusNr的组合仍存在重复,即使配置复合键,EF Core仍会因键唯一性校验失败报错。 - AsNoTracking():该方法仅关闭实体跟踪,不会取消键唯一性校验,因此无法解决问题。
- HasNoKey():必须完全移除所有
HasKey()配置才能生效,否则EF会优先使用键配置进行校验。
内容的提问来源于stack exchange,提问作者user3449139
相关产品推荐
相关产品推荐

