Расширение моего комментария в ответ для ясности.Дочерние записи могут содержать ссылку на своего родителя без родительской коллекции и могут выполняться с полем родительского идентификатора, указанным в дочернем поле, или без него:
public class ParentMap : EntityTypeConfiguration<Parent>
{
ToTable("Parents");
HasKey(x => x.Id)
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
//With Parent ID in Child:
public class ChildMap : EntityTypeConfiguration<Child>
{
ToTable("Children");
HasKey(x => x.ChildId)
.Property(x => x.ChildId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Parent)
.WithMany()
.HasForeignKey(x => CustomParentId);
}
// Without Parent ID in child. (Column in table called "CustomParentId")
public class ChildMap : EntityTypeConfiguration<Child>
{
ToTable("Children");
HasKey(x => x.ChildId)
.Property(x => x.ChildId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Parent)
.WithMany()
.Map(x => x.MapKey("CustomParentId"));
}
Одна важная деталь о ваших сущностях: в вашем дочернем классесвойство Parent должно быть помечено как виртуальное, чтобы включить отложенную загрузку.Например:
public class Child
{
// .. Without virtual
public Parent Parent { get; set; }
}
//vs.
public class Child
{
// .. With virtual
public virtual Parent Parent { get; set; }
}
Если для загрузки дочернего элемента вы используете следующий код ...
var child = myContext.Children
.SingleOrDefault(x => x.ChildId == 1);
, перейдите к родительскому элементу с помощью ...
string parentName = child.Parent?.Name;
В случае, если у вас нет виртуального родителя, ссылка Parent не была загружена, поэтому Parent будет #null.Если родительский объект является виртуальным, то для извлечения родительского объекта будет сделан отложенный вызов загрузки, и вы получите связанный объект.Ленивая загрузка в целом полезна, но не очень эффективна, поскольку приводит к вероятному второму обращению к базе данных.В обоих случаях, если вы сделали:
var child = myContext.Children
.Include(x => x.Parent)
.SingleOrdefault(x => x.ChildId == 1);
Тогда родительская ссылка будет загружена и доступна, виртуальная или нет.
В качестве альтернативы, лучший способ получения данных - использовать отдельное представлениемодель и выберите нужные поля из данных и связанных данных.Например, используя модель представления, которая возвращает дочерние данные с идентификатором и именем родителя:
var child = myContext.Children
.Select(x => new ChildSummaryViewModel
{
ChildId = x.ChildId,
Name = x.Name,
ParentId = x.Parent.Id,
ParentName = x.Parent.Name
})
.SingleOrdefault(x => x.ChildId == 1);
. Для этого не требуется .Include () для родителя, и будет загружаться информация из связанного родителя.В случае, когда у вас есть одна связанная сущность, это не будет казаться практичным, но в случаях, когда вы имеете дело с крупными сущностями со многими связанными сущностями, это может значительно сократить время запроса и риски запуска дополнительных запросов на отложенную загрузку, а также уменьшитьразмер данных, отправляемых обратно из базы данных.(а не целые сущности, только свойства, которые вас интересуют.)
При обновлении сущностей вы должны использовать связанные .Include () для корректировки или изменения отношений.