Поведение Entity Framework, приводящее к путанице при извлечении данных из БД в список

#entity-framework #linq

#entity-framework #linq

Вопрос:

Если я пытаюсь получить детали из машины в список деталей машин из БД, я выполняю это:

 Machine ma = new Machine();
ma = dbcontext.Machine.Where(s => s.Guid == guid).ToList()[0];

IQueryable<Part> PartsQuery = from m in db.Machines
                                    where m.Guid == guid
                                    from p in m.Parts
                                    select p;

ma.parts.AddRange(PartsQuery.ToList());
 

Я получаю в два раза больше деталей в моем списке деталей машины, чем на самом деле в базе данных!

Если я сделаю это вместо последней строки:

 List<parts> partsFromDb = PartsQuery.ToList();
ma.parts.AddRange(partsFromDb);
 

количество частей в списке ma.parts указано правильно. Может кто-нибудь объяснить это мне, пожалуйста?

Ответ №1:

Вы можете добиться того, что пытаетесь сделать, за один переход к вашей базе данных:

 Machine mab=context.Machine.Include(m=>m.Parts).FirstOrDefault(m=> m.Guid == guid); 
 

Что касается вашей проблемы, это, вероятно, связано с политикой кэширования EF, и, возможно, также связана с отложенной загрузкой. Я не знаю, как вы тестируете свой код, но если вы выполните следующие действия:

   Machine ma = context.Machine.FirstOrDefault(m=> m.Guid == guid); 

  IQueryable<Part> PartsQuery = from m in db.Machines
                                where m.Guid == guid
                                from p in m.Parts
                                select p;

 PartsQuery.ToList(); //materialize your query but don't save the resu<
 var parts=ma.parts;// now take a look here and you will see the related parts were loaded
 

Это должно быть причиной дублирования данных, потому что, когда вы выполняете свой запрос и позже обращаетесь к свойству навигации ( m.parts ), связанные объекты уже есть. Но в любом случае лучший способ получить то, что вам нужно, — это использовать запрос, который я показываю в начале моего ответа.

Ответ №2:

 Machine ma = new Machine();
ma = dbcontext.Machine.Where(s => s.Guid == guid).ToList()[0];

IQueryable<Part> PartsQuery = from m in db.Machines
                                    where m.Guid == guid
                                    from p in m.Parts
                                    select p;

ma.parts.AddRange(PartsQuery.ToList());
 

На 100% эквивалентно:

 // 1. Find and retrieve the first machine with the given GUID
Machine machine = dbcontext.Machine.First(s => s.Guid == guid);
// 2. Again, find and retrieve the machines with the given GUID, select the parts of each machine that matches and flatten it down to a single list.
IList<Part> machineParts = db.Machines
                                .Where(m => m.Guid == guid)
                                .SelectMany(m => m.Parts)
                                .ToList();  
// 3. Add.. all of the parts to that machine again?  
machine.parts.AddRange(machineParts);
 

Поэтому имеет смысл, что в итоге вы получаете двойные части внутри извлеченной машины.

Честно говоря, я не верю, что последнее изменение, о котором вы говорите, то есть преобразование ‘PartsQuery’ во временную переменную, имеет какое-либо значение в отношении конечного результата вашего machine .

Там должно происходить что-то еще.

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

1. Здравствуйте, почему это: IList<Part> machineParts содержит тот же список, что и этот: machine.parts для вас?

2. И да, он действительно ведет себя именно так, как я его описал. Что я забыл упомянуть, так это то, что я использую поставщика mysql entity framework.

3. извините, я сделал там опечатку. Простите меня, пожалуйста, конечно, я заменил списки. глупый я ^^

4. Ну, единственный сценарий, в котором machine.parts с шага 1 может отличаться machineParts от шага 2, — это если в базе данных есть несколько машин с одинаковым идентификатором GUID (и это звучит ужасно) или если вам удалось загрязнить EntityFramework DbContext двумя машинами с одинаковым идентификатором GUID. Но если вы правильно настроили свой DbContext (с помощью аннотаций свойств или конфигураций EntityType), то это должно быть невозможно.