EFCore — несколько неотслеживаемых экземпляров одного и того же объекта/сущности в свойстве списка возвращают ошибку о другом отслеживаемом экземпляре

#c# #asp.net-core #entity-framework-core #.net-6.0

#c# #asp.сетевое ядро #сущность-структура-ядро #.net-6.0

Вопрос:

Я не нашел никаких ресурсов по этому вопросу, вероятно, из-за неправильной формулировки.

Настройка — это объект Lake , который содержит список Fish . Каждая Fish Сущность также содержит Сущность своего Рода FishKind . Через какой-то API я получаю Lake объект, уже содержащий много рыбы. По какой-то причине существуют рыбы одного и того же вида — gt; поэтому у нас есть несколько объектов/экземпляров класса FishKind, которые имеют один и тот же идентификатор.

Вот некоторый фиктивный код об объекте Hirachy:

 class Lake {  public int LakeId;  public Listlt;Fishgt; Fish; } class Fish {  public int FishId;  public FishKind Kind; } class FishKind {  public int FishKindId;  FishKind(int id) { FishKindId = id; } } // this is how example data from the API could look like - I don't create objects like this in my code. I get them via the API // The FishKind have the same id/data however a different instances in C# // The FishKind have valid IDs which are also already in the DB // only Lake and Fish should be created var lake = new Lake(); var idOfShark = 29; lake.Fish.Add(new Fish("Tony", new FishKind(idOfShark))); lake.Fish.Add(new Fish("Helen", new FishKind(idOfShark)));  

Для дальнейшего разъяснения — пример данных, полученных через API

 {  "LakeId":0,  "Fish":[  {  "FishId":0,  "Name":"Tony",  "Kind":{  "FishKindId":29  }  },  {  "FishId":0,  "Name":"Helen",  "Kind":{  "FishKindId":29  }  }  ] }  
  • Когда я это делаю db.AddAsync(Lake) , это не удается, потому что у меня уже есть такая рыба, добавленная в базу данных
  • Когда я пытаюсь db.Update(Lake) , это работает до тех пор, пока у меня нет 2 рыбок одного и того же вида. Если они одного и того же рода, я получаю: The instance of entity type 'FishKind' cannot be tracked because another instance with the key value '{FishKindId: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.'

Один обходной путь, который пришел мне в голову, — это замена всех экземпляров FishKind уже отслеживаемым объектом, однако это кажется сложным и похожим на хакерское решение.

Комментарии:

1. @T. Трасудейн Я написал только минимальный код. Убедитесь, что у всех объектов есть идентификаторы, иначе я не смогу создать миграцию. Я отредактировал сообщение, чтобы добавить идентификаторы

2. @T. Трасудейн Я не хочу создавать новый вид рыб. Виды, которые, вероятно, уже существуют, образуют создание другого озера. (или причина в графическом интерфейсе при создании новой рыбы/озера я загружаю все виды из БД)

3. @T. Трасуден Я не хочу создавать новые виды. Но я получаю объекты через Rest, поэтому они отличаются. Вышеописанный обходной путь состоял бы в поиске всех рыбьих шкурок и замене их отслеживаемым объектом

4. Как насчет фактического кода, получающего данные из API, есть идеи, почему он создает новый экземпляр FishKind для каждой рыбы?

5. @T. Трасудейн, я думаю, это потому, что asp.net не знает, что объекты являются одними и теми же сущностями, и поэтому создает одинаковые объекты, но разные экземпляры

Ответ №1:

Посмотрите, где вы это делаете:

 lake.Fish.Add(new Fish("Tony", new FishKind(idOfShark)));  ^^^^^^^^^^^^^^^^^^^^^^^  

Не делайте этого; вы постоянно создаете новые объекты с тем же идентификатором, что и существующий объект, о котором контекст уже знает. Либо повторное использование, о котором знает сущность:

 var shark = context.FishKinds.Single(fk =gt; fk.FishKindId == 29); //if its local it will be returned, if it's not it will be fetched and remembered   lake.Fish.Add(new Fish("Tony", shark)); lake.Fish.Add(new Fish("Mary", shark));  

Или обновите объект Fish, чтобы у него был идентификатор FishKindId (или независимо от того, является ли имя столбца в базе данных, сопоставляемое с FiskKind PK), свойством int и установите для него значение 29 (idOfShark), оставьте объект FishKind нулевым.

Комментарии:

1. Это правильно, но я никогда new FishKind(idOfShark) не делаю этого с помощью JsonSerializer

2. О, я понимаю.. Обращение к сущностям БД потенциально может привести к этому. Я могу придумать множество способов решения, самым простым из которых может быть «поместить FishKindId в рыбу, перечислить рыбу, передающую из fish.FishKind. FishKindId в fisjh.FishKindId и установите значение FISH.FiskKind равным нулю

3. (По сути, восстановление графа объектов, чтобы быть чем-то, что может выдержать отслеживание изменений). Вы также можете перечислить рыбу, заменив все theFish.FishKind = context.FishKinds.Single(fk =gt; fk.FishKindId == theFish.FishKind.FishKindId)

4. Благодаря вашему ответу я наткнулся на ForeignKey("FishKindId")] то, что в настоящее время тестирую. Я могу изменить API таким образом, чтобы он давал мне только FishKindId, а не полный объект

5. Исправления опечаток в первоначальном комментарии: О, я понимаю.. Обращение к сущностям БД потенциально может привести к этому. Я могу придумать множество способов решения , самым простым из которых может быть «ввести FishKindId Fish , перечислить lake.Fish идентификатор переноса из lake.Fish.FishKind.FishKindId в lake.Fish.FishKindId и установить lake.Fish.FishKind значение null