Шрифт:
Интервал:
Закладка:
...
entity.HasMany(e=>e.Cars)
.WithOne(c=>c.MakeNavigation)
.HasForeignKey(c=>c.MakeId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Inventory_Makes_MakeId");
});
Отношения "один к одному"
Отношения "один к одному" конфигурируются аналогично, но только вместо метода WithMany() интерфейса Fluent API используется метод WithOne(). К зависимой сущности добавляется уникальный индекс. Вот код для отношения между сущностями Car и Radio, где применяется зависимая сущность (Radio):
modelBuilder.Entity<Radio>(entity =>
{
entity.HasIndex(e => e.CarId, "IX_Radios_CarId")
.IsUnique();
entity.HasOne(d => d.CarNavigation)
.WithOne(p => p.RadioNavigation)
.HasForeignKey<Radio>(d => d.CarId);
});
Даже если отношение определено в главной сущности, то к зависимой сущности все равно добавляется уникальный индекс. Далее приведен код установки отношения между сущностями Car и Radio, где для отношения используется главная сущность:
modelBuilder.Entity<Radio>(entity =>
{
entity.HasIndex(e => e.CarId, "IX_Radios_CarId")
.IsUnique();
});
modelBuilder.Entity<Car>(entity =>
{
entity.HasOne(d => d.RadioNavigation)
.WithOne(p => p.CarNavigation)
.HasForeignKey<Radio>(d => d.CarId);
});
Отношения "многие ко многим"
Отношения "многие ко многим" гораздо легче настраивать посредством Fluent API. Имена полей внешних ключей, имена индексов и каскадное поведение могут быть установлены в операторах, определяющих отношение. Ниже показан пример отношения "многие ко многим", переделанный с применением Fluent API (имена ключей и столбцов были изменены, чтобы улучшить читабельность):
modelBuilder.Entity<Car>()
.HasMany(p => p.Drivers)
.WithMany(p => p.Cars)
.UsingEntity<Dictionary<string, object>>(
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})"CarDriver",
j => j
.HasOne<Driver>()
.WithMany()
.HasForeignKey("DriverId")
.HasConstraintName("FK_CarDriver_Drivers_DriverId")
.OnDelete(DeleteBehavior.Cascade),
j => j
.HasOne<Car>()
.WithMany()
.HasForeignKey("CarId")
.HasConstraintName("FK_CarDriver_Cars_CarId")
.OnDelete(DeleteBehavior.ClientCascade));
Соглашения, аннотации данных и Fluent API — что выбрать?
В настоящий момент вас может интересовать, какой из вариантов следует выбирать для формирования ваших сущностей, а также их связей друг с другом и с хранилищем данных? Ответ: все три. Соглашения активны всегда (если только вы не переопределите их посредством аннотаций данных или Fluent API). С помощью аннотаций данных можно делать почти все то, на что способны методы Fluent API, и хранить информацию в самом сущностном классе, повышая в ряде случаев читабельность кода и удобство его сопровождения. Из трех вариантов наиболее мощным является Fluent API, но код скрыт в классе DbContext. Независимо от того, используете вы аннотации данных или Fluent API, имейте в виду, что аннотации данных переопределяют встроенные соглашения, а методы Fluent API переопределяют вообще все.
Выполнение запросов
Запросы на извлечение данных создаются посредством запросов LINQ в отношении свойств DbSet<T>. На стороне сервера механизм трансляции LINQ поставщика баз данных видоизменяет запрос LINQ с учетом специфичного для базы данных языка (скажем, Т-SQL). Запросы LINQ, охватывающие (или потенциально охватывающие) множество записей, не выполняются до тех пор, пока не начнется проход по результатам запросов (например, с применением foreach) или не произойдет привязка к элементу управления для их отображения (наподобие визуальной сетки данных). Такое отложенное выполнение позволяет строить запросы в коде, не испытывая проблем с производительностью из-за частого взаимодействия с базой данных.
Скажем, чтобы извлечь из базы данных все записи об автомобилях желтого цвета, запустите следующий запрос:
var cars = Context.Cars.Where(x=>x.Color == "Yellow");
Благодаря отложенному выполнению база данных фактически не запрашивается до тех пор, пока не начнется проход по результатам. Чтобы выполнить запрос немедленно, используйте ToList():
var cars = Context.Cars.Where(x=>x.Color == "Yellow").ToList();
Поскольку запросы не выполняются до их запуска, их можно строить в нескольких строках кода. Показанный ниже пример кода делает то же самое, что и предыдущий пример:
- Понимание SQL - Мартин Грубер - Базы данных