Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
dotnet ef database update TPT -c AutoLot.Samples.ApplicationDbContext
При обновлении базы данных исполняющая среда EF Core создаст следующие таблицы. Индексы также показывают, что таблицы имеют сопоставление "один к одному":
CREATE TABLE [dbo].[BaseEntities](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TimeStamp] [varbinary](max) NULL,
CONSTRAINT [PK_BaseEntities] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[Inventory](
[Id] [int] NOT NULL,
[MakeId] [int] NOT NULL,
[Color] [nvarchar](max) NULL,
[PetName] [nvarchar](max) NULL,
CONSTRAINT [PK_Inventory] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Inventory]
WITH CHECK ADD CONSTRAINT [FK_Inventory_BaseEntities_Id]
FOREIGN KEY([Id])
REFERENCES [dbo].[BaseEntities] ([Id])
GO
ALTER TABLE [dbo].[Inventory] CHECK CONSTRAINT [FK_Inventory_BaseEntities_Id]
GO
На заметку! С сопоставлением TPT связаны значительные последствия в плане производительности, которые должны приниматься во внимание при выборе данной схемы сопоставления. Дополнительные сведения ищите в документации: https://docs.microsoft.com/ru-ru/ef/core/performance/modeling-for-performance#inheritance-mapping.
Чтобы "сбросить" базу данных и проект для подготовки к следующему набору примеров, закомментируйте код в методе OnModelCreating() и опять удалите папку Migrations вместе с базой данных:
dotnet ef database drop -f -c AutoLot.Samples.ApplicationDbContext
Навигационные свойства и внешние ключи
Навигационные свойства представляют то, каким образом сущностные классы связаны между собой, и позволяют проходить от одного экземпляра сущности к другому. По определению навигационным является любое свойство, которое отображается на нескалярный тип, как определено поставщиком базы данных. На практике навигационное свойство сопоставляется с другой сущностью (навигационное свойство типа ссылки) или с коллекцией других сущностей (навигационное свойство типа коллекций). На стороне базы данных навигационные свойства транслируются в отношения внешнего ключа между таблицами. Инфраструктура EF Core напрямую поддерживает отношения вида "один к одному", "один ко многим" и (в версии EF Core 5) "многие ко многим". Сущностные классы также могут иметь обратные навигационные свойства с самими собой, представляя самоссылающиеся таблицы.
На заметку! Объекты с навигационными свойствами удобно рассматривать как связные списки. Если навигационные свойства являются двунаправленными, тогда объекты ведут себя подобно двусвязным спискам.
Прежде чем погружаться в детали навигационных свойств и шаблонов отношений между сущностями, ознакомьтесь с табл. 22.4, где приведены термины, которые используются во всем трех шаблонах отношений.
Отсутствие свойств для внешних ключей
Если сущность с навигационным свойством типа ссылки не имеет свойства для значения внешнего ключа, тогда EF Core создаст необходимое свойство или свойства внутри сущности. Они известны как теневые свойства внешних ключей и именуются в формате <имя навигационного свойства><имя свойства главного ключа> или <имя главной сущности><имя свойства главного ключа>. Сказанное справедливо для всех видов отношений ("один ко многим", "один к одному", "многие ко многим"). Такой подход к построению сущностей с явным свойством или свойствами внешних ключей гораздо яснее, чем поручение их создания исполняющей среде EF Core.
Отношения "один ко многим"
Чтобы создать отношение "один ко многим", сущностный класс со стороны "один" (главная сущность) добавляет свойство типа коллекции сущностных классов, находящихся на стороне "многие" (зависимые сущности). Зависимая сущность также должна иметь свойства для внешнего ключа обратно к главной сущности, иначе исполняющая среда EF Core создаст теневые свойства внешних ключей, как объяснялось ранее.
Например, в базе данных, созданной в главе 21, между таблицей Makes (представленной сущностным классом Make) и таблицей Inventory (представленной сущностным классом Car) имеется отношение "один ко многим". Для упрощения примеров сущность Car будет отображаться на таблицу Cars. В следующем коде показаны двунаправленные навигационные свойства, представляющие это отношение:
using System.Collections.Generic;
namespace AutoLot.Samples.Models
{
public class Make : BaseEntity
{
public string Name { get; set; }
public IEnumerable<Car> Cars { get; set; } = new List<Car>();
}
}
using System.Collections.Generic;
namespace AutoLot.Samples.Models
{
public class Car : BaseEntity
{
public string Color { get; set; }
public string PetName { get; set; }
public int MakeId { get; set; }
public Make MakeNavigation { get; set; }
}
}
На заметку! При создании шаблонов для существующей базы данных исполняющая среда EF Core именует навигационные свойства типа ссылок аналогично обычным свойствам (скажем, public Make {get; set;}). В итоге могут возникать проблемы с навигацией и IntelliSense, не говоря уже о затруднениях при работе с кодом. Для ясности предпочтительнее добавлять к именам навигационных свойств типа ссылок суффикс Navigation, как демонстрировалось выше.
В примере Car/Make сущность Car является зависимой ("многие" в "один ко многим"), а сущность Make — главной ("один" в "один ко многим").
Добавьте в класс ApplicationDbContext экземпляр DbSet<Make>:
public DbSet<Car> Cars { get; set; }
public DbSet<Make> Makes { get; set; }
Затем создайте миграцию и обновите базу данных с использованием приведенных далее команд:
dotnet ef migrations add One2Many -o Migrations
-c AutoLot.Samples.ApplicationDbContext
dotnet ef database update One2Many -c AutoLot.Samples.ApplicationDbContext
При обновлении базы данных с применением миграции EF Core создаются следующие таблицы:
CREATE TABLE [dbo].[Makes](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[TimeStamp] [varbinary](max) NULL,
CONSTRAINT [PK_Makes] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[Cars](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Color] [nvarchar](max)