[ Полезный рекламный блок ]
Попробуйте свои силы в игре, где ваши навыки программирования на 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 і ознайомтеся з умовами навчання, ми спеціалізуємося тільки на індивідуальних заняттях, як для початківців, так і для просунутих програмістів. Ви можете взяти як одне заняття для опрацювання питання, що вас цікавить, так і кілька, для більш щільної роботи. Завдяки особистому кабінету, кожен студент підвищить якість свого навчання, у вашому розпорядженні:
- Доступ до пройденого матеріалу
- Тематичні статті
- Бібліотека книг
- Онлайн тестування
- Спілкування в закритих групах