Когда я пытаюсь вставить / обновить записи, я получаю следующую ошибку.
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 и другие доступные параметры, но все же не могу решить эту проблему.
Любая помощь в этом приветствуется.
Хорошо .. Можете ли вы опубликовать образец кода, или если вы можете изменить мой существующий код, это будет хорошо
@ AmirReza-Farahlagha Согласен, но я не получаю никаких значений и не сохраняю их в объекте из БД. Я фактически передаю свой объект из бизнес-уровня в DAL.
@XamDev Вы установили увеличение идентичности в базе данных вашей таблицы ??
Нет .. Я устанавливаю первичный ключ из кода .. Вы можете увидеть приращение Id
@XamDev Если вы устанавливаете в базе данных инкрементную идентификацию, dbms контролируют ваши идентификаторы и устанавливают их как автоматические. Также вам не нужно контролировать свои идентификаторы в коде C#.
@ AmirReza-Farahlagha Согласен .. Но приложение не так структурировано .. Так что ищу этот вопрос ..
@XamDev Я знаю, но это не связано со структурой вашего кода. Сначала код вашей структуры?
Нет, сначала его БД
@XamDev Не волнуйся, чувак, сейчас я создаю для тебя образец.
Вы, по-видимому, никогда не попадаете в строку _objContext.Entry(lstDataCapDetailsRecords).State = EntityState.Detached;, потому что это вызовет исключение времени выполнения. В списке не может быть состояния объекта.





Эта ошибка возникает, когда вы хотите отслеживать изменения, внесенные в 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;
}
Да .. Список будет содержать смесь новых и существующих записей? В этом случае приведенный выше код будет работать?
Это дает мне ошибку. Предполагается, что операция с базой данных повлияет на 1 строку (строки), но на самом деле повлияла на 0 строк. Данные могли быть изменены или удалены после загрузки объектов. Сейчас я прохожу миксы
Ошибка при добавлении новых записей или при обновлении?
Можете ли вы попробовать один раз со всеми новыми записями и один раз со всеми обновлениями?
Его смешанные записи ... Я думаю, это может быть для обновленных записей ... s это IsSuccess = _objContext.SaveChanges ()> 0; единственная строка в коде, которая сохраняет / обновляет записи
Если вы хотите иметь таблицу с 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 Нет, вы не можете, вам нужно перейти к конструктору в таблице и сосредоточиться на столбце, который вы хотите увеличить, после этого, вниз по странице у вас есть свойства столбца, в Спецификации идентификации установите `` да '', а также вы можете установить свой Инкрементальные как 1,2,3, ... каждое число, которое вы хотите.
Итак, вы пытаетесь сказать, что если для столбца первичного ключа не задано значение identity = true, то вставка и обновление записей одним единственным методом не будет работать?
@XamDev Именно так. Это одна из проблем, которые могут решить вашу проблему.
@XamDev Дод получишь результат ??
Да .. Это произошло из-за того, что в списке присутствуют повторяющиеся первичные ключи .. Итак, после получения уникальных первичных ключей появилась возможность вставлять / обновлять записи .. Спасибо за помощь!
Похоже, вам нужно использовать
Attach(). Я не уверен, что вы можете сделать_objContext.Attach(lstDataCapDetails), но, по крайней мере, вы должны подключиться к существующему_objContextи установитьHasIndex()сIsUnique()для любых уникальных индексов.