Прикрепление объекта типа "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;
}