代码之家  ›  专栏  ›  技术社区  ›  XamDev

在asp.net核心中插入/更新记录时出错

  •  3
  • XamDev  · 技术社区  · 7 年前

    无法跟踪实体类型的实例,因为另一个实例 与{Id}具有相同键值的已被跟踪。

    下面是我的代码。在这里,我创建/生成ID(主键),增量为1。在Save&处都出现错误;更新

    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和其他可用的选项,但仍然无法解决这个问题。

    谢谢你在这方面的任何帮助。

    4 回复  |  直到 7 年前
        1
  •  2
  •   Barr J    7 年前

    首先,很多人错过了这些检查,从而导致运行时异常。 因此,您应该始终检查实体的状态:

    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() 方法,这在任何情况下都是不推荐的,除非你真的必须这样做。

        2
  •  5
  •   user229044    7 年前

    如果你想和我一起吃饭的话 Primary-Key 还有 Identity-Incremental 你必须创造你的 Table 凝固后 ForeignKey 你必须把 为了这个 . 比如:

    enter image description here

    对于此问题,您的代码将更改为:

        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;
        }
    
        3
  •  2
  •   LazZiya    7 年前

    我看到您对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;
        }
    
        4
  •  0
  •   Siavash    7 年前

    model ,而不是真正保持一个未追踪的 在里面 memory . 我有一个小建议或实际上是一个替代的建议方法,可以解决你的问题。

    EntityFramework 将自动跟踪更改。但你可以 Ovverride SaveChanges() 在你的 DbContext .

    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();
    }
    
    推荐文章