Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Чтобы у нас в базе данных уже были начальные данные при запуске приложения, нам нужен некий класс. который бы выполнял роль инициализатора базы данных. Для этого добавим в проект новый класс, который назовем SampleData и который будет иметь следующий код:
using System.Linq; using MobileStore.Models; namespace MobileStore { public static class SampleData { public static void Initialize(MobileContext context) { if (!context.Phones.Any()) { context.Phones.AddRange( new Phone { Name = "iPhone X", Company = "Apple", Price = 600 }, new Phone { Name = "Samsung Galaxy Edge", Company = "Samsung", Price = 550 }, new Phone { Name = "Pixel 3", Company = "Google", Price = 500 } ); context.SaveChanges(); } } } }
Данный класс определяет один статический метод Initialize()
, в котором происходит добавление трех начальных элементов - объектов Phone.
Для добавления объектов в бд в метод Initialize передается контекст данных. И если данные в таблице Phones в бд отсутствуют (if (!context.Phones.Any())
), то добавляются три объекта.
Чтобы инициализатор базы данных вызывался при старте приложения, изменим класс Program следующим образом:
using System; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MobileStore.Models; namespace MobileStore { public class Program { public static void Main(string[] args) { var host = CreateHostBuilder(args).Build(); using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; try { var context = services.GetRequiredService<MobileContext>(); SampleData.Initialize(context); } catch (Exception ex) { var logger = services.GetRequiredService<ILogger<Program>>(); logger.LogError(ex, "An error occurred seeding the DB."); } } host.Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } }
В данном случае получаем контекст из сервисов и вызываем инициализатор. В случае ошибки осуществляем логгирования через соответствующий сервис.
Итак, начальные данные определены, и теперь мы хотим их выводить на веб-страницу, чтобы пользователи видели набор товаров и смогли бы один из них выбрать.
Для этого перейдем к папке Controllers, где находится единственный контроллер HomeController. Изменим его содержимое следующим образом:
using System.Linq; using Microsoft.AspNetCore.Mvc; using MobileStore.Models; namespace MobileStore.Controllers { public class HomeController : Controller { MobileContext db; public HomeController(MobileContext context) { db = context; } public IActionResult Index() { return View(db.Phones.ToList()); } } }
Во-первых, здесь удалены все ненужные нам методы - все кроме метода Index, который будет использоваться для передачи пользователю данных о товарах.
Во-вторых, добавлен конструктор, в котором получаем контекст данных. Здесь применяется встроенный механизм внедрения зависимостей. Так как, в методе ConfigureServices()
контекст данных был добавлен в качестве сервиса:
services.AddDbContext<MobileContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
То через конструктор мы можем получить эту зависимость и использовать ее.
Затем вызывается метод View()
, который генерирует представление. И в этот метод передаются все объекты
из таблицы Phones в базе данных. Для передачи данных нам достаточно использовать такую конструкцию: db.Phones.ToList()
.
За построение визуального интерфейса в MVC отвечают представления. И теперь создадим само представление для вывода списка смартфонов. Для представлений в проекте предназначена папка Views. По умолчанию в этой папке уже есть подкаталог для представлений контроллера Home, в котором три представления: About.cshtml, Contact.cshtml и Index.chtml.
Каждый метод контроллера по умоланию использует одноименное представление. То есть метод Index будет использовать представление Index.cshtml. Откроем это представление и изменим его код следующим образом:
@model IEnumerable<MobileStore.Models.Phone> @{ //ViewData["Title"] = "Список смартфонов"; Layout = null; } <!DOCTYPE html> <html> <head> <title>Магазин смартфонов</title> </head> <body> <h3>Смартфоны</h3> <table> <tr> <td>Модель</td> <td>Производитель</td> <td>Цена</td> <td></td> </tr> @foreach (var phone in Model) { <tr> <td>@phone.Name</td> <td>@phone.Company</td> <td>@phone.Price</td> <td><a href="~/Home/Buy/@phone.Id">Купить</a></td> </tr> } </table> </body> </html>
Представления напоминают html-страницы, так как могут содержать и часто содержат очень много кода html. Но кроме собственно html они также содержат специальные инструкции, которые предваряются символом @. Это инструкции синтаксиса Razor - специального движка представлений, который позволяет использовать вместе с html и код на языке c#. Далее мы подробнее разберем синтаксис движка Razor, а пока достаточно знать, что после символа @ идут выражения на языке C#.
Первая строка устанавливает модель представления - та сущность, которая будет доступна в представлении через объект Model. В данном случае это объект
IEnumerable<MobileStore.Models.Phone>
, так как в контроллере в методе Index мы передаем список смартфонов, а список - объект List представляет
интерфейс IEnumerable.
Далее идет блок кода, в котором выражение Layout = null
указывает, что мастер-страница не будет применяться к этому представлению. Далее мы добавим к нему мастер-страницу и узнаем, зачем она нужна, а пока обойдемся без нее.
Практически весь остальной код представляет собой стандартный код веб-страницы на языке html: создание обычной таблицы, которая выводит информацию о продаваемых смартфонах. Здесь
также используется конструкция @foreach (var phone in Model)
. То есть тут мы создаем цикл. В нем мы пробегаемся по всем элементам в объекте
Model, который представляет переданный в методе Index список смартфонов. И затем получаем значение свойства каждого элемента с помощью синтаксиса Razor:
@phone.Name
и помещаем его в ячейку таблицы.
В последнюю колонку таблицы для каждого элемента добавляется ссылка <a href="~/Home/Buy/@phone.Id">Купить</a>
. При нажатии на эту ссылку методу Buy контроллера HomeController
будет отправляться запрос, в котором вместо @phone.Id будет указан id смартфона. Пока у нас, правда, отсутствует метод Buy, но скоро мы его создадим.
В методе Configure()
класса Startup устанавливаются настройки маршрутизации приложения:
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
Здесь добавляется маршрут с именем default
. Поле pattern
указывает, что запрос к приложению должен иметь двух-трехсегментную структуру.
Вначале идет имя контроллера, потом имя метода и потом может идти необязательный параметр id.
То есть чтобы обратиться к контроллеру HomeController или отправить ему запрос, нам надо указать в строке запроса его имя - Home и далее через слеш указать метод контроллера, к которому отправляется запрос, например, Home/Index. По умолчанию при запуске проекта или при обращении к сайту система mvc будет вызывать действие Index контроллера HomeController.
Теперь запустим приложение на выполнение. По умолчанию, как и определено выше маршрутом default, сработает обращение к методу Index контроллера Home, который возвратит пользователю страницу с данными:
А через интерфейс Visual Studio в окне SQL Server Object Explorer мы можем увидеть созданную базу данных: