簡體   English   中英

在asp.net核心中插入/更新記錄時出錯

[英]Error while insert/update records in asp.net core

當我嘗試插入/更新記錄時,我收到以下錯誤。

無法跟蹤實體類型的實例,因為已經跟蹤了具有相同的{Id'}鍵值的另一個實例。

下面是我的相同代碼。 在這里,我以1為增量創建/生成ID(主鍵)。我在Save&Update中收到錯誤

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和其他可用選項,但我仍然無法解決此問題。

對此有任何幫助表示贊賞。

如果您想要使用Primary-KeyIdentity-IncrementalTable ,則必須在設置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;
    }

首先,很多人都缺少這些檢查,從而導致運行時異常。 所以你總是應該檢查你的實體的狀態:

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()方法的麻煩,除非你真的需要,否則不建議這樣做。

我看到你使用相同的方法添加和更新實體,我的第一個建議是單獨的關注點,並為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;
    }

如果要跟蹤對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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM