Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Ранее использованные нами модели Book и Purchase имели довольно простую структуру - одни простые свойства типа int или string. Например, рассмотрим следующие две модели:
public class Player { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string Position { get; set; } public int? TeamId { get; set; } public Team Team { get; set; } } public class Team { public int Id { get; set; } public string Name { get; set; } public string Coach { get; set; } public IEnumerable<Player> Players { get; set; } }
Здесь класс Player имеет свойство Team, которое показывает принадлежность игрока к определенной команде. Свойство Team
называется
навигационным свойством - при получении данных об игроке оно автоматически будет получать данные из БД. Однако для этого нам надо также
установить внешний ключ.
Внешний ключ состоит из двух свойств: навигационного и обычного. Навигационное мы рассмотрели выше. А обычное должно принимать одно из следующих вариантов имени:
Имя_навигационного_свойства+Имя ключа из связанной таблицы - в нашем случае имя навигационного свойства Team, а ключа из модели Team - Id,
поэтому в нашем случае нам надо обозвать свойство TeamId
, что собственно и было сделано в вышеприведенном коде.
Имя_класса_связанной_таблицы+Имя ключа из связанной таблицы - в нашем случае класс Team, а ключа из модели Team - Id,
поэтому опять же в этом случае получается TeamId
.
Теперь создадим контекст данных, использующий модели:
public class SoccerContext : DbContext { public DbSet<Player> Players { get; set; } public DbSet<Team> Teams { get; set; } }
Теперь посмотрим, как бы это все располагалось в БД. Допустим, у нас есть некоторая база данных SoccerInfo.mdf. Пусть наши модели Player и Team располагаются соответственно в таблицах Players и Teams.
Определение таблицы Teams, которая будет хранить объекты модели Team, выглядит следующим образом:
Скрипт создания таблицы:
CREATE TABLE [dbo].[Teams] ( [Id] INT NOT NULL PRIMARY KEY IDENTITY, [Name] NVARCHAR (50) NOT NULL, [Coach] NVARCHAR (50) NOT NULL );
Тогда чтобы проецировать модель Player на таблицу Players в базе данных, нам надо задать следующее определение столбцов таблицы:
Скрипт создания таблицы:
CREATE TABLE [dbo].[Players] ( [Id] INT NOT NULL PRIMARY KEY IDENTITY, [Name] NVARCHAR (50) NOT NULL, [Age] INT NOT NULL, [Position] NVARCHAR (50) NOT NULL, [TeamId] INT NULL, CONSTRAINT [FK_Players_Teams] FOREIGN KEY ([TeamId]) REFERENCES [Teams] ([Id]) ON DELETE SET NULL );
Кроме того, здесь мы задаем внешний ключ - свойство TeamId теперь будет ссылаться на свойство Id из таблицы Teams.
Чтобы задать внешний ключ, мы добавляем в панели SQL внизу под дизайнером таблицы следующую строку:
CONSTRAINT [FK_Players_Teams] FOREIGN KEY ([TeamId]) REFERENCES [Teams]([Id]) ON DELETE SET NULL
Это обычное выражение языка SQL, которое связывает столбцы двух таблиц.
Последняя часть этого выражения (ON DELETE SET NULL
) указывает, что при удалении объекта из таблицы Teams, свойству TeamId,
которое ссылалось на удаленный объект, будет присвоено значение null
.
Это надо, чтобы у нас игроки при удалении команд не относились больше к удаленным командам. Однако мы можем задать и другое действие,
например, при удалении команды удалить всех ее игроков. Для этого нам надо написать ON DELETE CASCADE
.
Теперь после определения таблиц наполним их некоторыми начальными данными. К примеру добавим некоторые данные в таблицу Teams:
И в таблицу Players (где столбец TeamId содержит некоторое существующее значение из столбца Id таблицы Teams):
Теперь перейдем к созданию логики приложения. Добавим в приложение контроллер и определим в нем вывод всех игроков на страницу:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using NavigationProperty.Models; using System.Data.Entity; using System.Data; namespace NavigationProperty.Controllers { public class HomeController : Controller { SoccerContext db = new SoccerContext(); // Выводим всех футболистов public ActionResult Index() { var players = db.Players.Include(p => p.Team); return View(players.ToList()); } } }
Теперь с помощью метода Include
фреймворк подгружает для каждого игрока также и команду, ассоциированную с определенным игроком.
А при выводе модели в представление Index.cshtml фреймворк будет выводить для каждого игрока название команды:
@model IEnumerable<NavigationProperty.Models.Player> @{ ViewBag.Title = "Каталог игроков"; } <h2>Каталог игроков</h2> <p> @Html.ActionLink("Добавить игрока", "Create") </p> <table> <tr> <th>Имя игрока</th> <th>Возраст</th> <th>Позиция на поле</th> <th>Команда</th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.DisplayFor(modelItem => item.Age) </td> <td> @Html.DisplayFor(modelItem => item.Position) </td> <td> @Html.DisplayFor(modelItem => item.Team.Name) </td> <td> @Html.ActionLink("Редактировать", "Edit", new { id = item.Id }) | @Html.ActionLink("Удалить", "Delete", new { id = item.Id }) </td> </tr> } </table> <p> @Html.ActionLink("Каталог команд", "ListTeams") </p>
Поскольку мы в контроллере с помощью метода Include получили связанный с игроком объект Team по навигационному свойству TeamId, то при передаче в контроллер всех объектов Player к ним также цепляются связанные с ними объекты Team, поэтому мы можем вполне использовать в представлении свойство item.Team.Name для получения имени команды.
Подобным образом можно вывести список команд. Но там все просто и не так интересно. Зато у нас в модели Team свойство Players, которое призвано хранить связанных с командой игроков. Используем его. Например, выведем все данные о команде, в том числе о ее игроках. Вначале добавим в контроллер следующий метод:
public ActionResult TeamDetails(int? id) { if (id == null) { return HttpNotFound(); } Team team = db.Teams.Find(id); if (team == null) { return HttpNotFound(); } team.Players = db.Players.Where(m=> m.TeamId== team.Id); return View(team); }
Во-первых, чтобы обработать ввод при отсутствии передаваемого значения, в качестве параметра используем int? id
.
Во-вторых, мы подгружаем всех игроков, связанных с командой, в выражении team.Players = db.Players.Where(m=> m.TeamId== team.Id)
.
Ну и представление TeamDetails.cshtml для отображения данных о команде могло бы выглядеть так:
@using NavigationProperty.Models @model Team @{ ViewBag.Title = "Команда " + @Model.Name; Layout = "~/Views/Shared/_Layout.cshtml"; } <div> <h4>Команда @Model.Name</h4> <hr /> <dl> <dt>Название</dt> <dd> @Html.DisplayFor(model => model.Name) </dd> <dt>Тренер</dt> <dd> @Html.DisplayFor(model => model.Coach) </dd> <dt>Игроки</dt> <dd> <ul> @foreach (Player player in Model.Players) { <li>@player.Name (@player.Position)</li> } </ul> </dd> </dl> </div>