Эта проблема может возникнуть, если присоединяемые объекты имеют свойства навигации, ссылающиеся на другие объекты. Пример:
public class Parent
{
public int Id { get; set; }
public Child Child { get; set; }
}
public class Child
{
public int Id { get; set; }
}
Следующий код выдаст исключение:
using (var context = new MyDbContext())
{
var parent = new Parent { Id = 1 };
var child1 = new Child { Id = 1 };
parent.Child = child1;
var child2 = new Child { Id = 1 }; // same key
context.Children.Attach(child2); // child with key = 1 is attached now
var objContext = ((IObjectContextAdapter)context).ObjectContext;
ObjectStateEntry entry;
bool isAttached = objContext.ObjectStateManager.TryGetObjectStateEntry(
new EntityKey("MyDbContext.Parents", "Id", parent.Id), out entry);
// isAttached will be false because a Parent with Id = 1 is not attached
if (!isAttached)
{
// we assume now that we could attach the parent safely
context.Parents.Attach(parent);
// Assumption is wrong -> Exception, because Attach attaches the whole
// object graph, so it tries also to attach child1 together with parent
// But child1 has the same key as child2 which is already attached
}
}
Итак, смысл в том, что TryGetObjectStateEntry
только проверяет состояние базового объекта и не учитывает никаких навигационных свойств. Attach
, с другой стороны, не только присоединяет базовую сущность, но и дочерние элементы, которые еще не присоединены, что приводит к исключению.