Для исходного типа «DbSet» было найдено несколько реализаций шаблона запроса. Двусмысленный призыв «Присоединиться»

#c# #.net

Вопрос:

 public Listlt;ClassParticipantViewModelgt; GetClassParticipants(string studentId) {  Listlt;ClassParticipantViewModelgt; list = new Listlt;ClassParticipantViewModelgt;();   var results = (from cp in _DbContext.ClassParticipants  join c in _DbContext.Classes  on cp.ClassId equals c.ClassId  where cp.StudentId.Equals(studentId)  select new ClassParticipantViewModel  {  //ClassParticipant  ClassParticipantId = cp.ClassParticipantId,  StudentId = cp.StudentId,  ClassParticipantCreatedOn = cp.CreatedOn,   //Class  ClassId = c.ClassId,  ClassCode = c.ClassCode,  Section = c.Section,  CourseCode = c.CourseCode,  Description = c.Description,  Units = c.Units,  CreatedBy = c.CreatedBy,  ClassCreatedOn = c.CreatedOn,  })  .ToList();   list = list.Concat(results).ToList();   return list.ToList(); }  

Ошибка соединения:

введите описание изображения здесь

Модели:

введите описание изображения здесь

Модель представления:

введите описание изображения здесь

Я разрабатываю веб-API с использованием .NET Core 3.0, затем я получил сообщение об ошибке, в котором говорится, что неоднозначный вызов «Присоединиться», этот код работает в других моих проектах. Я в замешательстве, если что-то не так, влияет ли соглашение об именовании моих моделей?

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

1. Судя по именам ваших сущностей, я ожидал бы, что между ними будут свойства навигации, что означало бы, что создание соединения вручную было бы ненужным.. Почему у класса нет, например ICollectionlt;ClassParticipantgt; ClassParticipants {get;set;} , а у участника класса есть Class Class{get;set;} ?

2. @CaiusJard Как я могу присоединиться к этим двум, если создание соединения не требуется? Мне нужна подробная информация о классе, основанная на идентификаторе студента.

3. Инициализация list в первой строке метода является избыточной.

4. @TsahiAsher Спасибо, исправлено, но все еще появляется неоднозначный вызов ошибки «Присоединиться».

5. Когда вы рассказали EF, как связаны два объекта (например, имея коллекцию участников класса в классе или имея класс в участнике класса), вы можете просто сказать context.Classes.Include(c =gt;c.ClassParticipants) , чтобы загрузить все классы и связанные с ними участники класса. Если вы используете связь в предложении where или select, она также автоматически присоединяется

Ответ №1:

Краткое введение в навигационные реквизиты в EF:

Если бы ваши занятия выглядели так:

 class Class {  ICollectionlt;ClassParticipantgt; ClassParticipants {get; set;}  ... } class ClassParticipant {  Class Class{get;set;}  Participant Participant{get;set;}  ... } class Participant{  ICollectionlt;ClassParticipantgt; ParticipantClasses {get; set;}  ... }  

EF смог бы понять, что это типичное соотношение 1:M:1 между классами:участники:участники — во многих классах много участников, и средняя таблица разбивает его.

После того, как вы выполнили сопоставление свойств, вы можете писать такие запросы, как:

 _DbContext.ClassParticipants.Select(cp =gt;  new ClassParticipantViewModel  {  //ClassParticipant  ClassParticipantId = cp.ClassParticipantId,  StudentId = cp.StudentId,  ClassParticipantCreatedOn = cp.CreatedOn,   //Class  ClassId = cp.Class.ClassId,  ClassCode = cp.Class.ClassCode,  Section = cp.Class.Section,  CourseCode = cp.Class.CourseCode,  Description = cp.Class.Description,  Units = cp.Class.Units,  CreatedBy = cp.Class.CreatedBy,  ClassCreatedOn = cp.Class.CreatedOn,  } ).ToList();  

EF увидит, как вы перемещаетесь в select ( cp.Class.ClassId ), и будет знать, что он должен присоединиться к классам, чтобы получить нужные вам данные. Вы также можете сделать прямые навигационные ссылки аналогичным образом в Where предложении для загрузки связанных данных, или, если вы знаете , что не будете ссылаться на связанные данные в Select или Where , но захотите, чтобы данные были загружены, чтобы позже вы могли ссылаться на них в C#, вы можете использовать Include:

 _DbContext.ClassParticipants.Include(cp =gt; cp.Class).ToList();  

примечание, вы можете захотеть, например ICollectionlt;ClassParticipantgt; ClassParticipants {get; set;} = new HashSetlt;ClassParticipantgt;(); , потому, что это облегчает создание новых объектов, не забывая сначала создавать коллекцию:

 var c = new Class(...); var p = new Participant(...);  //can do this without having to remember to make a new collection c.ClassParticipants.Add(new ClassParticipant { Class = c, Participant = p });  

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

1. По моему опыту EF6, я бы настоятельно рекомендовал создать временную ссылку на Class , например let c = cp.Class , в запросе LINQ, а затем использовать c.ClassId и т. Д., В противном случае каждый вызов cp.Class.... будет добавлять JOIN оператор в сгенерированный SQL, снижая вашу производительность. Я не знаю, как это сделать в синтаксисе вызова метода.

2. @TsahiAsher только что проверил это утверждение на EFCore 5 и не увидел никаких признаков того, что сейчас это так; для моих тестов создается и повторно используется одно СОЕДИНЕНИЕ

3. Тогда, может быть, они улучшили его в EFCore.