Ошибка при вставке / обновлении записей в ядре asp.net

Когда я пытаюсь вставить / обновить записи, я получаю следующую ошибку.

The instance of entity type cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.

Ниже мой код для того же. Здесь я создаю / генерирую идентификатор (первичный ключ), увеличивая его на 1. Я получаю сообщение об ошибке как при сохранении, так и при обновлении.

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() для любых уникальных индексов.

Tetsuya Yamamoto 10.10.2018 06:42

Хорошо .. Можете ли вы опубликовать образец кода, или если вы можете изменить мой существующий код, это будет хорошо

XamDev 10.10.2018 06:54

@ AmirReza-Farahlagha Согласен, но я не получаю никаких значений и не сохраняю их в объекте из БД. Я фактически передаю свой объект из бизнес-уровня в DAL.

XamDev 13.10.2018 08:49

@XamDev Вы установили увеличение идентичности в базе данных вашей таблицы ??

AmirReza-Farahlagha 13.10.2018 08:55

Нет .. Я устанавливаю первичный ключ из кода .. Вы можете увидеть приращение Id

XamDev 13.10.2018 08:57

@XamDev Если вы устанавливаете в базе данных инкрементную идентификацию, dbms контролируют ваши идентификаторы и устанавливают их как автоматические. Также вам не нужно контролировать свои идентификаторы в коде C#.

AmirReza-Farahlagha 13.10.2018 09:04

@ AmirReza-Farahlagha Согласен .. Но приложение не так структурировано .. Так что ищу этот вопрос ..

XamDev 13.10.2018 09:35

@XamDev Я знаю, но это не связано со структурой вашего кода. Сначала код вашей структуры?

AmirReza-Farahlagha 13.10.2018 09:44

Нет, сначала его БД

XamDev 13.10.2018 09:45

@XamDev Не волнуйся, чувак, сейчас я создаю для тебя образец.

AmirReza-Farahlagha 13.10.2018 10:40

Вы, по-видимому, никогда не попадаете в строку _objContext.Entry(lstDataCapDetailsRecords).State = EntityState.Detached;, потому что это вызовет исключение времени выполнения. В списке не может быть состояния объекта.

Gert Arnold 13.10.2018 12:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
11
2 095
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Эта ошибка возникает, когда вы хотите отслеживать изменения, внесенные в model, а не оставлять не отслеживаемый model в memory. У меня есть небольшое предложение или альтернативный подход, который решит вашу проблему.

EntityFramework автоматически отслеживает изменения. Но вы можете OvverrideSaveChanges() в вашем 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();
}
Ответ принят как подходящий

Прежде всего, многие люди пропускают эти проверки, что приводит к исключениям во время выполнения. Поэтому вы всегда должны проверять состояние своей сущности:

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(), что не рекомендуется ни в коем случае, если вам действительно не нужно.

Я вижу, что вы используете один и тот же метод для объектов «Добавить» и «Обновить», мое первое предложение - это отдельные проблемы и сделать разные методы: один для добавления, а другой - для обновления, если это возможно.

С другой стороны, неясно, может ли список записей «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;
    }

Да .. Список будет содержать смесь новых и существующих записей? В этом случае приведенный выше код будет работать?

XamDev 13.10.2018 08:48

Это дает мне ошибку. Предполагается, что операция с базой данных повлияет на 1 строку (строки), но на самом деле повлияла на 0 строк. Данные могли быть изменены или удалены после загрузки объектов. Сейчас я прохожу миксы

XamDev 13.10.2018 09:00

Ошибка при добавлении новых записей или при обновлении?

LazZiya 13.10.2018 09:49

Можете ли вы попробовать один раз со всеми новыми записями и один раз со всеми обновлениями?

LazZiya 13.10.2018 09:50

Его смешанные записи ... Я думаю, это может быть для обновленных записей ... s это IsSuccess = _objContext.SaveChanges ()> 0; единственная строка в коде, которая сохраняет / обновляет записи

XamDev 13.10.2018 09:50

Если вы хотите иметь таблицу с Primary-Key, а также с Identity-Incremental, вы должны создать свой Table после установки ForeignKey, вы должны установить Identity-Incremental для этого ForeignKey. нравиться:

С этой проблемой ваш код изменится на этот:

    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 13.10.2018 11:08

@XamDev Нет, вы не можете, вам нужно перейти к конструктору в таблице и сосредоточиться на столбце, который вы хотите увеличить, после этого, вниз по странице у вас есть свойства столбца, в Спецификации идентификации установите `` да '', а также вы можете установить свой Инкрементальные как 1,2,3, ... каждое число, которое вы хотите.

AmirReza-Farahlagha 13.10.2018 11:14

Итак, вы пытаетесь сказать, что если для столбца первичного ключа не задано значение identity = true, то вставка и обновление записей одним единственным методом не будет работать?

XamDev 13.10.2018 11:18

@XamDev Именно так. Это одна из проблем, которые могут решить вашу проблему.

AmirReza-Farahlagha 13.10.2018 11:24

@XamDev Дод получишь результат ??

AmirReza-Farahlagha 13.10.2018 19:16

Да .. Это произошло из-за того, что в списке присутствуют повторяющиеся первичные ключи .. Итак, после получения уникальных первичных ключей появилась возможность вставлять / обновлять записи .. Спасибо за помощь!

XamDev 13.10.2018 19:27

Другие вопросы по теме