Прикрепление объекта типа "X" не удалось, поскольку другой объект того же типа

Я наткнулся на странную ошибку в моем коде. Это работало раньше, но теперь работает иногда.

Я использую EF6 для редактирования объекта с некоторыми отношениями. Чтобы не изменять отношения, я их прикрепляю (см. Пример кода).

public void EditA(A ThisIsA, B ThisIsB)
    {
        using (var Context = new LDZ_DEVEntities())
        {
            Context.As.Attach(ThisIsA);

            var b = Context.Bs.FirstOrDefault(x => x.BId == ThisIsB.BId);
            //var b = Context.Bs.Find(ThisIsB.BId);

            if (b != null)
                Context.Bs.Attach(b);
            else
                b = ThisIsB;

            if (b.C != null)
                Context.Cs.Attach(b.C);

            ThisIsA.Bs.Add(b);

            Context.SaveChanges();

        }
    }

Я редактировал имена, чтобы они были простыми.

Следующая строка

Context.Cs.Attach(b.C);

вызывает эту ошибку:

Прикрепление объекта типа 'C' не удалось, поскольку другой объект того же типа уже имеет одно и то же значение первичного ключа. Это может произойти при использовании метода "Прикрепить" или установки состояния объекта в "Без изменений" или "Модифицировано", если любые объекты на графике имеют конфликтующие значения ключей. Это может быть связано с тем, что некоторые объекты являются новыми и еще не получили значения ключей базы данных. В этом случае используйте метод "Добавить" или "Добавленное" состояние объекта для отслеживания графика, а затем установите состояние не новых объектов на "Без изменений" или "Модифицировано" по мере необходимости.

Эта строка была введена, поскольку все объекты C являются статическими объектами. Я никогда не хочу, чтобы C был создан. Если я удалю эту строку, каждый раз, когда я добавлю B в A; создается C. Что нежелательно.

Дополнительная информация:
A имеет список B's
B имеет один C

Этот метод EditA() вызывается в нескольких местах моего программного обеспечения. Эта ошибка появляется только при вызове метода в цикле (импорт). Во время работы над первой записью проблем нет. Но Im получает ошибку в записях после первого.

Я прочитал эти вопросы плюс ответы, но они не работали для меня:

Ответы

Ответ 1

Я исправил его.

В Фабио Луз его ответ, он сказал:

//если A загружен из контекста
        // не присоединяем его
        // если он был создан вне контекста
        //Context.Entry(ThisIsA).State = EntityState.Modified;

Это заставило меня задуматься, поэтому я отредактировал свой код:

public void EditA(A ThisIsA, B ThisIsB)
{
    using (var Context = new LDZ_DEVEntities())
    {
        var a = Context.As.Find(ThisIsA.AId);

        //var b = Context.Bs.FirstOrDefault(x => x.BId == ThisIsB.BId);
        var b = Context.Bs.Find(ThisIsB.BId);

        if (b != null)
            Context.Bs.Attach(b);
        else
            b = ThisIsB;

        if (b.C != null)
            Context.Cs.Attach(b.C);

        a.Bs.Add(b);

        Context.SaveChanges();

    }
}

Сводка изменений:

  • Изменено FirstOrDefault для поиска
  • Получить A из контекста

Сначала я удалил Attach of C, в результате это создало новый объект. Поэтому я изменил это изменение.

Особая благодарность Фабио Лузу. Я не мог бы сделать это без вашей помощи!

Ответ 2

Взгляните на следующую ссылку https://msdn.microsoft.com/en-us/data/jj592676.aspx

Если у вас есть сущность, которая, как вы знаете, уже существует в базе данных, но какие изменения могут быть сделаны, вы можете указать контексту, чтобы привязать объект и установить его состояние в Модифицированное. Например:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" }; 

using (var context = new BloggingContext()) 
{ 
    context.Entry(existingBlog).State = EntityState.Modified; 

    // Do some more work...  

    context.SaveChanges(); 
}

ПРИМЕЧАНИЕ: вам не нужно делать это со всеми объектами (A, B и C), просто с A.

РЕДАКТИРОВАТЬ 1

На основе вашего комментария попробуйте следующее:

//check if 
var _b = Context.Bs.Find(ThisIsB.BId);

if (_b != null)
  //b doesn't exist, then add to the context
  //make sure that the primary key of A is set.
  //_b.PrimaryKeyOfA = someValue;
  Context.Bs.Add(_b);
else
 //b already exists, then modify the properties
 //make sure that the primary key of A is set.

Context.SaveChanges();

РЕДАКТИРОВАТЬ 2

Я не тестировал, но он должен работать.

public void EditA(A ThisIsA, B ThisIsB)
{
    using (var Context = new LDZ_DEVEntities())
    {
        //if A has been loaded from context
        //dont attach it
        //if it has been created outside of the context
        //Context.Entry(ThisIsA).State = EntityState.Modified;

        var _b = Context.Bs.Find(ThisIsB.BId);

        if (_b == null)
        { 
            _b = ThisIsB;
        }

        ThisIsA.Bs.Add(_b);

        Context.SaveChanges();

    }
}

Ответ 3

Другим способом, в зависимости от вашей ситуации, является просто отсоединить состояние Entity.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Modify(Model model)
{

if (model.Image == null)
{
Model item = db.Model.Find(model.Name);

// Get the Content needed:
model.Image = item.Image;

// Detach the Comparison State:
db.Entry(item).State = EntityState.Detached;
}

if (ModelState.IsValid)
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}

return View(model);
}

Посредством этого: db.Entry(item).State = EntityState.Detached; состояние EntityFramework все еще остается в силе, и вы можете сохранить изменения в базе данных (db).

Надеюсь, это поможет!

Ответ 4

Приложить или установить сущность для модификации, не работает в моем случае, но эта работа для меня:

public int GuardarAsociacionesBonos(List<O_Bono_ConfiguracionPago> bonos)
{
    if (!bonos.Any()) return bonos.Count;

    using (var contexto = new TouchERPEntities())
    {
        foreach (var bono in bonos)
        {
           var existe = contexto.O_Bono_ConfiguracionPago.SingleOrDefault(x => x.IDBono == bono.IDBono && x.IDConfiguracionPago == bono.IDConfiguracionPago);
            if (existe != null && existe.IDBonoConfigPago != 0)
            {
                bono.IDBonoConfigPago = existe.IDBonoConfigPago;
                contexto.Entry(existe).CurrentValues.SetValues(bono);
            }
            else
            {
                contexto.O_Bono_ConfiguracionPago.Add(bono);
            }

        }

        contexto.SaveChanges();

    }

    return bonos.Count;
}