Объект с тем же ключом уже существует в ObjectStateManager. ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом

В принципе, у меня есть таблица, которая содержит несколько свойств для компании. Это таблица "master", и их идентификатор используется во многих других таблицах. Я в основном нахожу их идентификатор с помощью этого метода:

private Company currentcompany()
    {
        Company cuco = db.Companies.Single(x => x.username == User.Identity.Name);
        return cuco;
    }

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

Использование данных Tamper в Firefox (и я думаю, Fidler/многие другие), я мог бы легко изменить скрытый идентификатор и изменить другие данные компаний.

Чтобы остановить это, я добавил следующие строки в действие изменения:

        Company cuco = currentcompany();

        if (company.id != cuco.id)
        {
            return Content("Security Error");
        }

(FYI - Company - это модель /POCO, представляющая компанию, а сама Company - это данные формы.)

После добавления этого, если я отредактирую идентификатор в данных формы, он работает как ожидалось и вызывает "Ошибка безопасности", однако, если нет ошибки, и я продолжаю, я получаю ошибку в вопросе.

"Объект с тем же ключом уже существует в ObjectStateManager. ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом.

Я считаю, что это потому, что EF каким-то образом обнаруживает и удерживает первые данные, но я просто не уверен, как их исправить.

Любые советы?

Edit- --update -

Если вы можете понять, чего я пытаюсь достичь, есть ли лучший способ обойти это?

Ответ 1

Если вы загружаете объект из контекста, вы не можете снова присоединить объект с тем же ключом. Первый объект по-прежнему хранится во внутреннем кэше контекста, и контекст может содержать только один экземпляр с заданным значением ключа для каждого типа (он называется идентификационной картой и Я описал его здесь в других ситуация).

Вы можете решить это, отделив прежний экземпляр, но вам этого не нужно. Если вам нужно сохранить только новые значения, вы можете использовать это:

  • API ObjectContext: context.YourEntitySet.ApplyCurrentValues(newEntity);
  • API-интерфейс DbContext: context.Entry(oldEntity).CurrentValues.SetValues(newEntity);

Ответ 2

Немного о помощи для вас, если вы не знаете, как найти oldEntity, как в соответствии с Ladislav:

var entityKey = context.NewEntitySet.Create().GetType().GetProperty("Id").GetValue(newEntity);

factory.Entry(context.Set<NewEntityType>().Find(entityKey)).CurrentValues.SetValues(newEntity);

Ответ 3

Как ясно говорится об ошибке: вам нужно убедиться, что получить существующий элемент и изменить данные этого элемента с обновленной информацией и сохранить его. Вы не столкнетесь с этой проблемой, если вы обновите очень конкретную информацию, а не ключевую информацию, такую ​​как ID и ForeignKey ID
Ниже код должен выполнить эту работу!

 public virtual void Update(TEntity entity)
    {
        _context.Entry(entity).State = EntityState.Modified;

        this._context.SaveChanges();
    }

Использование будет -

 public async Task<bool> UpdateVendorItem(string userId, Vendor modified)
    {

        try
        {
            var existing = await this.GetVendors().SingleOrDefaultAsync(a => a.Id == modified.Id);


            //Set updated info
            existing.VendorName = modified.VendorName;

            //Update address information
            existing.Address.AddressLine1 = modified.Address.AddressLine1;
            ... 

            await _vendorRepository.UpdateAsync(existing);

    ...

Ответ 4

Только что увидел этот вопрос вчера. Перед обновлением необходимо отключить cuco. Проверьте решение в этом ответе