Ответ 1
В Entity Framework вы можете работать с ассоциациями внешних ключей. То есть внешний ключ к другому объекту выражается как пара двух свойств: свойство примитивного внешнего ключа (например, NominalRouting.OrderItemId
) и ссылка на объект (NominalRouting.OrderItem
).
Это означает, что вы можете либо установить примитивное значение, либо ссылку на объект, чтобы установить ассоциацию внешних ключей. Если вы установите один из них, EF пытается по возможности синхронизировать другой. К сожалению, это может также привести к конфликтам между примитивными значениями внешнего ключа и их сопроводительными ссылками.
Трудно сказать, что именно происходит в вашем случае. Однако я знаю, что ваш подход "копирования" объектов от одного родителя к другому... не идеален. Во-первых, никогда не рекомендуется менять значения первичного ключа. Установив их в 0
, вы сделаете объект похожим на новый, но это не так. Во-вторых, вы много раз назначаете одни и те же дочерние объекты другим родительским объектам. Я думаю, что, как следствие, вы получаете большое количество объектов, имеющих значение внешнего ключа, но не ссылку.
Я сказал "копирование", потому что это то, что вы, похоже, пытаетесь достичь. Если это так, вы должны правильно клонировать объекты и Add
их для каждого targetOrderItem
. В то же время мне интересно, почему вы (по-видимому) клонируете все эти объекты. Похоже, здесь здесь более уместны ассоциации со многими. Но это другой предмет.
Теперь ваш реальный вопрос: как найти конфликтующие ассоциации?
Это очень, очень сложно. Это потребует кода для поиска по концептуальной модели и поиска свойств, связанных с ассоциациями внешних ключей. Тогда вам придется найти свои ценности и найти несоответствия. Достаточно сложный, но тривиальный по сравнению с определением, когда возможный конфликт является фактическим конфликтом. Позвольте мне пояснить это двумя примерами. Здесь класс OrderItem
имеет требуемую ассоциацию внешних ключей, состоящую из свойств Order
и OrderId
.
var item = new OrderItem { OrderId = 1, ... };
db.OrderItems.Add(item);
db.SaveChanges();
Итак, существует элемент с OrderId
и Order
= null, а EF счастлив.
var item = db.OrderItems.Include(x => x.Order).Find(10);
// returns an OrderItem with OrderId = 1
item.Order = null;
db.SaveChanges();
Опять же, элемент с OrderId
назначен и Order
= null, но EF выдает исключение "Связь не может быть изменена...".
(и возможны более конфликтные ситуации)
Таким образом, вам не достаточно искать непревзойденные значения в парах OrderId/Order
, вам также нужно будет проверять состояния сущностей и точно знать, в какой комбинации состояний несоответствие не допускается. Мой совет: забудьте об этом, исправьте свой код.
Там один грязный трюк. Когда EF пытается сопоставить значения и ссылки внешнего ключа, где-то глубоко в дереве вложенных if
он собирает конфликты, о которых мы говорим, в членную переменную ObjectStateManager
с именем _entriesWithConceptualNulls
. Можно получить его значение, сделав некоторое отражение:
#if DEBUG
db.ChangeTracker.DetectChanges(); // Force EF to match associations.
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
var objectStateManager = objectContext.ObjectStateManager;
var fieldInfo = objectStateManager.GetType().GetField("_entriesWithConceptualNulls", BindingFlags.Instance | BindingFlags.NonPublic);
var conceptualNulls = fieldInfo.GetValue(objectStateManager);
#endif
conceptualNulls
является HashSet<EntityEntry>
, EntityEntry
является внутренним классом, поэтому вы можете только проверять коллекцию в отладчике, чтобы получить представление о конфликтующих объектах. Только для диагностики!!!