在 asp.net core 中插入/更新记录时出错
Posted
技术标签:
【中文标题】在 asp.net core 中插入/更新记录时出错【英文标题】:Error while insert/update records in asp.net core 【发布时间】:2019-03-14 21:52:21 【问题描述】:当我尝试插入/更新记录时,出现以下错误。
无法跟踪实体类型的实例,因为另一个实例 'Id' 具有相同键值的已被跟踪。
下面是我的代码。在这里,我以 1 为增量创建/生成 ID(主键)。我在保存和更新时都遇到错误
public bool SaveDataCapDetails(List<TDataCapDetails> lstDataCapDetails)
bool IsSuccess = false;
using (var dbContextTransaction = _objContext.Database.BeginTransaction())
try
List<TDataCapDetails> lstDataCapDetailsRecords = null;
if (lstDataCapDetails.Where(x => x.Id == 0).Count() > 0)
lstDataCapDetailsRecords = new List<TDataCapDetails>();
lstDataCapDetailsRecords.InsertRange(0, lstDataCapDetails);
int? id = _objContext.TDataCapDetails.Max(x => (int?)x.Id);
id = id == null ? 0 : id;
foreach (var item in lstDataCapDetailsRecords.Where(x => x.Id == 0))
id = id + 1;
item.Id = (int)id;
_objContext.Entry(lstDataCapDetailsRecords).State = EntityState.Detached;
_objContext.AddRange(lstDataCapDetailsRecords);
_objContext.SaveChanges();
if (lstDataCapDetails.Where(x => x.Id > 0).Count() > 0)
lstDataCapDetailsRecords = new List<TDataCapDetails>();
lstDataCapDetailsRecords = lstDataCapDetails.Where(x => x.Id > 0).ToList();
_objContext.UpdateRange(lstDataCapDetailsRecords);
_objContext.SaveChanges();
dbContextTransaction.Commit();
catch (Exception ex)
dbContextTransaction.Rollback();
throw ex;
return IsSuccess;
我从下面的业务层调用上述方法
bool success = dal.SaveDataCapDetails(lstDataCapDetails)
我已尝试使用 AsNoTracking 和其他可用选项,但仍无法解决此问题。
对此的任何帮助表示赞赏。
【问题讨论】:
听起来你需要使用Attach()
。我不确定你是否可以使用_objContext.Attach(lstDataCapDetails)
,但至少你应该附加到现有的_objContext
并设置HasIndex()
和IsUnique()
以获得任何唯一索引。
好的..你能发布示例代码吗,或者如果你可以在我现有的代码中修改就可以了
@AmirReza-Farahlagha 同意但我没有从数据库中获取任何值并存储在对象中。我实际上是在将我的对象从业务层传递给 DAL。
@XamDev 你在你的表的数据库中设置了身份增加吗??
Nope..我正在从代码中设置主键..你可以看到Id的增量
【参考方案1】:
如果你想拥有Primary-Key
和Identity-Incremental
的表,你必须在设置ForeignKey
之后创建Table
,你必须为ForeignKey
设置Identity-Incremental
。喜欢:
有了这个问题,您的代码更改为:
public bool SaveDataCapDetails(List<TDataCapDetails> lstDataCapDetails)
bool IsSuccess = false;
using (var dbContextTransaction = _objContext.Database.BeginTransaction())
try
List<TDataCapDetails> lstDataCapDetailsRecords = null;
if (lstDataCapDetails.Where(x => x.Id == 0).Count() > 0)
lstDataCapDetailsRecords = new List<TDataCapDetails>();
_objContext.AddRange(lstDataCapDetailsRecords);
_objContext.SaveChanges();
if (lstDataCapDetails.Where(x => x.Id > 0).Count() > 0)
lstDataCapDetailsRecords = new List<TDataCapDetails>();
lstDataCapDetailsRecords = lstDataCapDetails.Where(x => x.Id > 0).ToList();
_objContext.UpdateRange(lstDataCapDetailsRecords);
_objContext.SaveChanges();
dbContextTransaction.Commit();
catch (Exception ex)
dbContextTransaction.Rollback();
throw ex;
return IsSuccess;
【讨论】:
好的...但是我不能没有 Identity set = true ? @XamDev 不,你不能,你必须去桌子上的设计师,并专注于你想要增加的列,然后,在页面的下方你有列属性,在身份规范集'是的,您还可以将增量设置为 1,2,3,... 您想要的每个数字。 所以你想说如果主键列没有设置为identity = true,那么在一个方法中插入和更新记录将不起作用? @XamDev 没错。这是可以解决您的问题的问题之一。 @XamDev 你得到结果了吗??【参考方案2】:首先,很多人缺少这些检查,从而导致运行时异常。 所以你总是应该检查你的实体的状态:
context.YourEntities.Local.Any(e => e.Id == id);
或者
context.ChangeTracker.Entries<YourEntity>().Any(e => e.Entity.Id == id);
为了确保你的实体是'安全使用',如果你想要一个方便的使用方法,你可以使用这个:
/// <summary>
/// Determines whether the specified entity key is attached is attached.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="key">The key.</param>
/// <returns>
/// <c>true</c> if the specified context is attached; otherwise, <c>false</c>.
/// </returns>
internal static bool IsAttached(this ObjectContext context, EntityKey key)
if (key == null)
throw new ArgumentNullException("key");
ObjectStateEntry entry;
if (context.ObjectStateManager.TryGetObjectStateEntry(key, out entry))
return (entry.State != EntityState.Detached);
return false;
然后:
if (!_objectContext.IsAttached(entity.EntityKey))
_objectContext.Attach(entity);
这将为您省去重写SaveChanges()
方法的麻烦,在任何情况下都不建议这样做,除非您真的必须这样做。
【讨论】:
【参考方案3】:我看到您对 Add 和 Update 实体使用相同的方法,我的第一个建议是分开关注点,并为 Add 使用不同的方法,如果可能的话,为 Update 使用另一种方法。
另一方面,不清楚记录列表“lstDataCapDetails”是否可能包含所有新记录或新记录和现有记录的混合以进行更新,在第二种情况下,您的代码会给您错误,因为您可以尝试将 Id 分配给现有记录,或者您可以尝试更新全新的记录。
您可以通过检查实体是否被跟踪并分离它来克服错误,然后附加修改后的实体并更新它。
在这里您可以看到您的方法的修改版本:
public bool SaveDataCapDetails(List<TDataCapDetails> lstDataCapDetails)
bool IsSuccess = false;
using (var dbContextTransaction = _objContext.Database.BeginTransaction())
try
int? id = _objContext.TDataCapDetails.Max(x => (int?)x.Id);
id = id == null ? 0 : id;
// entities with Id == 0 --> new entities
// you may need to check if Id == null as well (depends on your data model)
var entitiesToAdd = lstDataCapDetails.Where(x => x.Id == 0);
foreach(var entity in entitiesToAdd)
entity.Id = id++;
// new entities is not tracked, its state can be changed to Added
_objContext.Entry(entity).State = EntityState.Added;
// entities with Id > 0 is already exists in db and needs to be updated
var entitiesToUpdate = lstDataCapDetails.Where(x => x.Id > 0);
foreach (var entity in entitiesToUpdate)
// check if entity is being tracked
var local = _objContext.Set<TDataCapDetails>().Local.FirstOrDefault(x => x.Id.Equals(entity.Id));
// if entity is tracked detach it from context
if (local != null)
_objContext.Entry<TDataCapDetails>(local).State = EntityState.Detached;
// attach modified entity and change its state to modified
_objContext.Attach(entity).State = EntityState.Modified;
// optional: assign value for IsSuccess
IsSuccess = _objContext.SaveChanges() > 0;
dbContextTransaction.Commit();
catch (Exception ex)
dbContextTransaction.Rollback();
throw ex;
return IsSuccess;
【讨论】:
是的.. 该列表将包含新记录和现有记录的混合?在这种情况下,上面的代码可以工作吗? 它给了我错误数据库操作预计会影响 1 行,但实际上影响了 0 行。自加载实体以来,数据可能已被修改或删除。现在我正在传递混合记录 添加新记录或更新时出错? 您可以尝试一次所有新记录,然后一次更新所有记录吗? 它的混合记录..我认为它可能是用于更新记录...这是 IsSuccess = _objContext.SaveChanges() > 0;仅在代码中保存/更新记录的行【参考方案4】:当您想要跟踪对model
所做的更改,而不是在memory
中实际保留未跟踪的model
时,会发生此错误。我有一个小建议或实际上可以解决您的问题的替代建议方法。
EntityFramework
将自动跟踪更改。但你可以在你的DbContext
中Ovverride
SaveChanges()
。
public override int SaveChanges()
foreach (var ent in ChangeTracker.Entries<Client>())
if (ent.State == EntityState.Modified)
// Get the changed values
var modifiedProps = ObjectStateManager.GetObjectStateEntry(ent.EntityKey).GetModifiedProperties();
var currentValues = ObjectStateManager.GetObjectStateEntry(ent.EntityKey).CurrentValues;
foreach (var propName in modifiedProps)
var newValue = currentValues[propName];
//log your changes
return base.SaveChanges();
【讨论】:
以上是关于在 asp.net core 中插入/更新记录时出错的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ASP.NET Core 更新由依赖表组成的记录 [关闭]
当在 asp.net 中插入更新的记录而不刷新页面时,如何自动更新 Gridview?
在 ASP.NET Core 中呈现 RDLC 报告时出现索引超出范围异常