[ Полезный рекламный блок ]
Попробуйте свои силы в игре, где ваши навыки программирования на C# станут решающим фактором. Переходите по ссылке 🔰.
В этой статьей рассмотрим процесс построения реалистичного проекта, который продемонстрирует совместное использование ASP.NET Core MVC и Entity Framework Core. Проект будет простым, но близким к реальности, и фокусироваться на часто применяемых средствах Entity Framework Core.
Приложение будет управлять базой данных TV Shows, а его главная страница в конце будет выглядеть, как показано ниже.
В этом проекте, я покажу альтернативные способы создания контроллеров на основе «scaffolding tools».
Создание приложения с использованием ASP.NET Core MVC и EF Core
Создание и настройка проекта
Чтобы создать проект TvShows, запустите Visual Studio и выберите в меню File (Файл) — New Project (Создать Проект). Укажите шаблон проекта ASP.NET Core Web Application (Веб-приложение ASP.NET Core). Введите TvShows, в поле Name, на следующей странице укажите Framework .Net 7.0 и нажмите кнопку Create:
На данный момент у нас есть начальный проект. Давайте запустим проект (Crtl + F5), чтобы убедиться, что все в порядке. Ниже вы можете изменить браузер, с помощью которого вы хотите запустить сайт:
После запуска мы получаем страницу приветствия, как показано ниже:
MVC вызывает классы контроллеров (и методы действий в них) в зависимости от входящего URL. Логика маршрутизации URL, используемая MVC по умолчанию, использует такой формат, чтобы определить, какой код следует вызвать:
1 |
/[Controller]/[ActionName]/[Parameters] |
Формат маршрутизации задается в файле Program.cs:
1 2 3 |
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); |
Когда вы переходите к приложению и не указываете никаких сегментов URL, оно по умолчанию использует контроллер HomeController и метод Index, указанный выше. Таким образом, если вы введете https://localhost:XXXX/Home/Index в качестве URL, вы получите ту же страницу приветствия, которая показана выше.
Модель и Репозиторий
Модель для приложения TVShows будет основана на списке фильмов. Для этого реализуем класс TvShow. Мы будем использовать этот класс с Entity Framework Core (EF Core) для работы с базой данных. EF Core — это фреймворк объектно-реляционного отображения (ORM), который упрощает код доступа к данным. Классы моделей не имеют зависимости от EF Core. Они просто определяют свойства данных, которые будут храниться в базе данных.
Создайте папку Models и добавьте в нее файл класса по имени TvShow.cs со следующим содержимым:
1 2 3 4 5 6 7 8 9 10 11 12 |
public class TvShow { public int Id { get; set; } public string Title { get; set; } public Genre Genre { get; set; } public decimal Rating { get; set; } public string ImdbUrl { get; set; } } |
Затем добавьте еще один класс под названием Genre.cs, как показано ниже:
1 2 3 4 5 6 7 8 9 10 |
public enum Genre { Drama, Comedy, Romance, [Display(Name = "Romantic Comedy")] RomCom, Crime, Mystery } |
Далее создадим контроллер и представления (часть View-Controller приложения MVC). По умолчанию в нашем проекте, уже есть контроллер – HomeController.cs, удалим его и добавим снова.
Щелкните правой кнопкой мыши папку Controllers и выберите Add -> Controller. В следующем окне выберите «MVC Controller with views, using Entity Framework» и нажмите Add:
В следующем окне выберите TvShow в качестве класса «Model» и нажмите знак + в классе «Data context» и дайте ему имя ApplicationContext. Оставьте значения по умолчанию для остальных полей и выберите — Добавить:
Если в ходе добавления возникла ошибка, со следующим содержимым:
«install the package microsoft.visualstudio.web.codegeneration.design and try again»
Перейдите в Dependencies – Manage NuGet Packages – Browse и укажите в поиске:
1 |
Microsoft.VisualStudio.Web.CodeGeneration.Design |
Установите данную библиотеку. ВАЖНО! Если библиотека уже установлена и ошибка по-прежнему возникает, попробуйте пересобрать проект или же переустановить данную библиотеку вручную.
На этом этапе работает инструмент scaffolding. Автоматическое создание контекста базы данных и методов и представлений CRUD (create, read, update, and delete) действий известно как scaffolding.
Рассмотрим, что Visual Studio добавила в наш проект автоматически в результате использования строительных лесов.
Следующие методы являются методами действия контроллера TvShowsController:
- Index (GET)
- Details (GET)
- Create (GET & POST)
- Edit (GET & POST)
- Delete (GET & POST)
Файлы представления Razor для страниц: Создать, Удалить, Подробности, Редактировать и Индекс, так же были созданы по адресу – Views / TvShows.
Автоматически были скачаны библиотеки необходимые для работы с Entity Framework Core:
В корне проекта можно увидеть новую папку Data, со сгенерированным классом ApplicationContext:
1 2 3 4 5 6 7 8 9 |
public class ApplicationContext : DbContext { public ApplicationContext (DbContextOptions<ApplicationContext> options) : base(options) { } public DbSet<TvShow> TvShow { get; set; } = default!; } |
ApplicationContext координирует функциональность EF Core (Create, Read, Update, Delete и т.д.) для модели TvShow. TvShowsContext является производным от Microsoft.EntityFrameworkCore.DbContext. Контекст данных определяет, какие сущности включены в модель данных.
Имя строки подключения передается в контекст путем вызова метода на объекте DbContextOptions. Для локальной разработки система конфигурации ASP.NET Core считывает строку подключения из файла appsettings.json, который уже успешно сгенерирован с необходимым содержимым, перейдем в файл и измени название базы данных на TvShowsDb:
1 2 3 |
"ConnectionStrings": { "ApplicationContext": "Server=(localdb)\\mssqllocaldb;Database=TvShowsDb;Trusted_Connection=True;MultipleActiveResultSets=true" } |
ASP.NET Core построен с использованием инъекции зависимостей (DI). Службы (например, контекст БД EF Core) регистрируются с помощью DI во время запуска приложения. Компоненты, которым требуются эти службы, получают их через параметры конструктора:
1 2 3 |
builder.Services.AddDbContext<ApplicationContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("ApplicationContext") ?? throw new InvalidOperationException("Connection string 'ApplicationContext' not found."))); |
В контроллере Controllers / TvShowsController.cs конструктор использует внедрение зависимостей для внедрения контекста базы данных (TvShowsContext) в контроллер:
1 2 3 4 5 6 7 8 9 10 |
public class TvShowsController : Controller { private readonly ApplicationContext _context; public TvShowsController(ApplicationContext context) { _context = context; } //... } |
Создание базы данных
Создадим базу данных с помощью функции EF Core Migrations. Миграции позволяет нам создать базу данных, соответствующую нашей модели данных, и обновлять схему базы данных при изменении модели данных.
Откройте Tools -> NuGet Package Manager > Package Manager Console (PMC) и выполните следующую команду в PMC:
1 |
PM> Add-Migration Initial |
Команда Add-Migration генерирует код для создания начальной схемы базы данных, которая основана на модели, указанной в классе TvShowsContext. Аргумент Initial — это имя миграции, можно использовать любое имя.
После выполнения команды в папке Migrations будет создан файл миграции:
В качестве следующего шага выполните следующую команду в Package Manager Console:
1 |
PM> Update-Database |
Команда Update-Database запускает метод Up в файле Migrations/{time-stamp}_InitialCreate.cs, который создает базу данных.
Теперь проверим созданную базу данных. Откройте View -> Sql Server Object Explorer. Вы увидите только что созданную базу данных по следующему пути:
Как вы видите, таблица TvShows и таблица истории миграций создаются автоматически. Затем в таблицу истории миграций вставляется запись, чтобы показать выполненные миграции в базе данных.
Создание первой записи
Так как начальный контроллер HomeController был удален, а вместо него был создан новый – TvShowsController. Необходимо немного подправить систему маршрутизации, для этого перейдем в файл Program.cs и изменим начальный маршрут:
1 2 3 |
app.MapControllerRoute( name: "default", pattern: "{controller=TvShows}/{action=Index}/{id?}"); |
Запустите приложение, на главной странице нажмите по ссылке — Create New.
На данный момент выпадающий список Genre пуст. Мы отредактируем Create.cshtml, чтобы загрузить список из перечисления Genre, как показано ниже:
1 2 3 4 5 6 |
<div class="form-group"> <label asp-for="Genre" class="control-label"></label> @*<select asp-for="Genre" class="form-control"></select>*@ <select asp-for="Genre" asp-items="Html.GetEnumSelectList<Genre>()" class="form-control"></select> <span asp-validation-for="Genre" class="text-danger"></span> </div> |
После сохранения Create.cshtml, если обновить браузер, изменения будут отражены, а выпадающий список жанра загружен. Теперь мы можем создать первую запись. (Аналогичные изменения следует сделать и в Edit.cshtml):
После нажатия кнопки Create, новая запись отображается на странице Index, как показано ниже:
Теперь мы будем показывать ImdbUrl как ссылку. Измените Views / TvShows / Index.cshtml следующим образом:
1 2 3 4 |
<td> @*@Html.DisplayFor(modelItem => item.ImdbUrl)*@ <a href="@item.ImdbUrl" target="_blank">@item.Title</a> </td> |
Добавление аннотаций в модель
В этом разделе добавим аннотации данных в нашу модель для целей проверки и отображения. Аннотации данных предоставляют встроенный набор атрибутов проверки, которые вы можете декларативно применить к любому классу или свойству. Он также содержит атрибуты форматирования, которые помогают в форматировании.
Изменим класс TvShow.cs, следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System.ComponentModel.DataAnnotations; namespace TvShows.Models { public class TvShow { public int Id { get; set; } [Required(ErrorMessage = "Enter movie 'Title'")] [StringLength(50, MinimumLength = 3)] public string? Title { get; set; } public Genre Genre { get; set; } [Required(ErrorMessage = "Enter movie 'Rating'")] public decimal? Rating { get; set; } [Required(ErrorMessage = "Enter movie 'Imdb Url'")] [DataType(DataType.Url)] [Display(Name = "Imdb Url")] public string? ImdbUrl { get; set; } } } |
Если запустим приложение и попытаемся создать новую запись, как показано ниже, мы сможем увидеть эффект от этих аннотаций данных:
Ошибки внедряются как на стороне клиента (с помощью JavaScript и jQuery), так и на стороне сервера (в случае, если у пользователя отключен JavaScript).
Добавление нового свойства в модель
Теперь мы добавим свойство ImageUrl в наше приложение и покажем в записях постер телешоу. Реализовывать загрузку изображения на сервер в этом примере не станем, ограничимся обычной ссылкой. Процесс загрузку изображения на сервер, рассмотрим в последующих уроках.
Во-первых, добавим это свойство в модель TvShow:
1 2 3 4 |
[Required(ErrorMessage = "Enter movie 'Poster'")] [DataType(DataType.Url)] [Display(Name = "Poster")] public string? ImageUrl { get; set; } |
Далее мы будем использовать Code First Migrations для добавления этого поля в таблицу db. Откройте Tools -> NuGet Package Manager > Package Manager Console (PMC) и выполните следующую команду в PMC:
1 |
PM> Add-Migration AddImageUrl |
После, команду:
1 |
PM> Update-Database |
Теперь обновим атрибут [Bind] для методов Create и Edit POST в контроллере TvShowsController.cs:
1 |
[Bind("Id,Title,Genre,Rating,ImdbUrl,ImageUrl")] |
Далее нам необходимо изменить представления Index, Create, Edit, чтобы отобразить это новое поле.
Добавьте в файл Edit.cshtml и Create.cshtml следующую часть кода:
1 2 3 4 5 |
<div class="form-group"> <label asp-for="ImageUrl" class="control-label"></label> <input asp-for="ImageUrl" class="form-control" /> <span asp-validation-for="ImageUrl" class="text-danger"></span> </div> |
Так же, изменим файл Index.cshtml следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<table class="table"> <thead> <tr> <th> @Html.DisplayNameFor(model => model.ImageUrl) </th> <th> @Html.DisplayNameFor(model => model.Genre) </th> <th> @Html.DisplayNameFor(model => model.Rating) </th> <th> @Html.DisplayNameFor(model => model.ImdbUrl) </th> <th></th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> @if (item.ImageUrl is not null && item.ImageUrl.Length > 0) { <img src="@Url.Content(item.ImageUrl)" alt="@item.Title" /> } </td> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Genre) </td> <td> @Html.DisplayFor(modelItem => item.Rating) </td> <td> @*@Html.DisplayFor(modelItem => item.ImdbUrl)*@ <a href="@item.ImdbUrl" target="_blank">@item.Title</a> </td> <td> <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> | <a asp-action="Details" asp-route-id="@item.Id">Details</a> | <a asp-action="Delete" asp-route-id="@item.Id">Delete</a> </td> </tr> } </tbody> </table> |
В верхней части файла Index.cstml, добавим стилей для корректного отображения постеров фильмов:
1 2 3 4 5 6 |
<style> img{ width:180px; height:250px; } </style> |
Запустите приложение и выполните редактирование текущей записи, добавив к ней ссылку на изображение, попробуйте добавить новую запись. После, перейдите на главную страницу и посмотрите результат:
Добавление компонента статистики
Напоследок, добавим View Component, для отображения статистики по фильмам. В корне проекта создадим папку Components и добавим в нее новый класс StatisticsViewComponent.cs, со следующим содержимым:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class StatisticsViewComponent : ViewComponent { private readonly ApplicationContext _context; public StatisticsViewComponent(ApplicationContext context) { _context = context; } public async Task<IViewComponentResult> InvokeAsync() { var genreCount = await _context.TvShow.GroupBy(e => e.Genre).Select(e => new { Key = e.Key, Count = e.Count() }).ToDictionaryAsync(k => k.Key, c => c.Count); return View(new StatisticsViewModel { GenreCount = genreCount, TotalCount = genreCount.Values.Sum() }); } } |
В методе InvokeAsync(), считаем общее количество фильмов, а так же сгруппировав их по жанрам. В качестве модели, передаем в частичное представление Default.cshtml, которое мы создадим чуть позже.
В корне проекта, создадим папку ViewModels, в ней определим класс StatisticsViewModel.cs, со следующим содержимым:
1 2 3 4 5 |
public class StatisticsViewModel { public int TotalCount { get; set; } public Dictionary<Genre, int> GenreCount { get; set; } } |
Теперь создадим представление, которое будет выводить переданные из компонента данные. Добавим в папку Views / Shared новый каталог, который назовем Components. А в этот каталог добавим папку, которая называется по имени нашего компонента — Statistics, и определим в ней новое представление Default.cshtml со следующим содержимым:
1 2 3 4 5 6 7 8 9 10 |
@using TvShows.ViewModels; @model StatisticsViewModel <ul class="list-group"> <li class="list-group-item active">Total TV shows: @Model.TotalCount</li> @foreach (KeyValuePair<Genre, int> item in Model.GenreCount) { <li class="list-group-item">@item.Key - @item.Value</li> } </ul> |
На странице Index.cshtml, вызовем компонент с помощью тег-хелпера vc:
1 |
<vc:statistics></vc:statistics> |
Однако перед использованием тега необходимо зарегистрировать наш компонент в качестве tag-хелпера. Для этого в файл _ViewImports.cshtml в папке Views добавим следующую директиву:
1 |
@addTagHelper *, TvShows |
Название сборки проекта, как правило, совпадает с названием самого проекта.
Запустим приложение и посмотрим на результат:
По такому принципу вы можете создавать собственные компоненты для ваших веб-сайтов.
На этом разработка проекта с использованием технологий ASP.NET Core MVC и EF Core подошла к концу.
Вы можете скачать исходный код в моем репозитории — Github.
Я надеюсь, что вам понравилось читать эту статью, и она оказалась легкой для понимания. Пожалуйста, дайте мне знать, если у вас есть какие-либо комментарии или исправления.
Так же вам может быть интересна предыдущая статья — Генерация robots.txt в Asp.Net.
Вы хотите научится писать код на языке программирования C#?
Создавать различные информационные системы, состоящие из сайтов, мобильных клиентов, десктопных приложений, телеграмм-ботов и т.д.
Переходите к нам на страницу Dijix и ознакомьтесь с условиями обучения, мы специализируемся только на индивидуальных занятиях, как для начинающих, так и для более продвинутых программистов. Вы можете взять как одно занятие для проработки интересующего Вас вопроса, так и несколько, для более плотной работы. Благодаря личному кабинету, каждый студент повысит качество своего обучения, в вашем распоряжении:
- Доступ к пройденному материалу
- Тематические статьи
- Библиотека книг
- Онлайн тестирование
- Общение в закрытых группах
Живи в своем мире, программируй в нашем.
Спасибо за крутую информацию, сложно сейчас найти годные step-by-step статьи с обьяснением. Огромная благодарность!
Просто и гениально одновременно. Теперь я понимаю в целом структуру подобного рода проектов. Спасибо!!!