Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Ранее мы использовали относительно простые модели Book и Purchase. Но в реальных приложениях большинство моделей, как правило, оказываются гораздо сложнее по структуре. Например, создадим две следующие модели, представляющие футболиста и футбольную команду:
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 ICollection<Player> Players { get; set; } public Team() { Players = new List<Player>(); } }
Кроме стандартных свойств типа string класс 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, выглядит следующим образом:
А таблица Players будет иметь следующую структуру:
В отличие от таблицы Teams здесь мы также задаем внешний ключ - свойство 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; 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 ко всем моделям Player подключается свой объект Team по навигационному свойству TeamId, то то в представлении мы можем получить этот связанный объект Team и использовать его свойства, например, получить item.Team.Name для отображения имени команды.
Подобным образом можно вывести список команд. Но там все просто и не так интересно. Зато у нас в модели Team свойство Players, которое призвано хранить связанных с командой игроков. Используем его. Например, выведем все данные о команде, в том числе о ее игроках. Вначале добавим в контроллер следующий метод:
public ActionResult TeamDetails(int? id) { if (id == null) { return HttpNotFound(); } Team team = db.Teams.Include(t=>t.Players).FirstOrDefault(t=>t.Id==id); if (team == null) { return HttpNotFound(); } return View(team); }
Во-первых, чтобы обработать ввод при отсуствии передаваемого значения, в качестве параметра используем int? id
.
Во-вторых, мы подгружаем всех игроков, связанных с командой, в выражении db.Teams.Include(t=>t.Players).FirstOrDefault(t=>t.Id==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>