在ASP.NET Core中插入/更新记录时出错

时间:2018-10-10 04:13:23

标签: c# entity-framework linq asp.net-core

当我尝试插入/更新记录时,出现以下错误。

  

无法跟踪实体类型的实例,因为另一个实例   具有与{'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和其他可用选项,但仍然无法解决此问题。

对此有任何帮助。

4 个答案:

答案 0 :(得分:3)

如果您想同时拥有Primary-KeyIdentity-Incremental的表格,则必须在设置Table之后创建ForeignKey,而必须将Identity-Incremental设置为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;
    }

答案 1 :(得分: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()方法的麻烦,除非确实需要,否则在任何情况下都不建议这样做。

答案 2 :(得分:2)

我看到您对Add和Update实体使用相同的方法,我的第一个建议是单独的关注点,并为Add添加一个不同的方法,并在可能的情况下为Update选择另一个方法。

另一方面,不清楚记录列表“ lstDataCapDetails”是否包含所有新记录或新记录和现有记录的混合以进行更新,在第二种情况下,您的代码将给您错误,因为您可以尝试将ID分配给现有记录,或者您可以尝试更新全新的记录。

通过检查是否跟踪该实体并将其分离,然后附加修改后的实体并对其进行更新,可以解决该错误。

在这里您可以看到方法的修改版本:

waitpid()

答案 3 :(得分:0)

当您要跟踪对.and().addFilter(new UsernamePasswordAuthenticationFilter() { @Override protected String obtainPassword(HttpServletRequest request) { String password = super.obtainPassword(request); return null != password ? password.toLowerCase() : null; } }) 所做的更改,而不是实际上将未跟踪的model保留在model中时,会发生此错误。我有一点建议,或者实际上是一种替代建议的方法,可以解决您的问题。

memory将自动跟踪更改。但是您可以在EntityFrameworkOvverride SaveChanges()

DbContext