[ Полезный рекламный блок ]
Попробуйте свои силы в игре, где ваши навыки программирования на C# станут решающим фактором. Переходите по ссылке 🔰.
В этой статье, мы рассмотрим процесс публикации Asp.Net Core приложения, на Windows Хостинг. В качестве хостинга может выступать любой из популярных вариантов:
- reg.ru
- www.smarterasp.net
- hostpro.ua
- godaddy.com
Мной был выбран хостинг — hostpro.ua. Я ожидаю от вас знания концепций объектно-ориентированного программирования на C# и минимальные знания работы с хостинг провайдерами. Я предполагаю, что вы также знаете концепции .NET Core, особенно паттерн MVC.
Для примера будем использовать приложение по управлению пользователями. База данных Sql Server с управлением через Entity Framework.
Как опубликовать приложение Asp.Net Core
Создание проекта
Откройте Visual Studio 2022.
Создайте проект «Asp.Net Core Web Application(.Net Core)» с шаблоном MVC Template. В графе “Authentication type”, оставляем значение None.
В задачу входит разработать возможность добавлять, просматривать, редактировать и удалять пользователей из базы данных.
- В папку Models, добавим класс User:
1 2 3 4 5 6 7 8 9 10 11 |
public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } |
2. Скачаем библиотеку: Microsoft.EntityFrameworkCore.SqlServer
3. В папку Models, добавим класс ApplicationContext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { Database.EnsureCreated(); } } |
5. Далее в файле конфигурации appsettings.json определим настройки подключения к БД, которая будет хранить данные пользователей. Для этого на сервере, создадим базу данных и передадим ее данные для подключения в файл:
При создании пользователя базы данных, отметьте галочку, что пользователь имеет полный доступ ко всей ветке:
Сам файл appsettings.json теперь содержит строку подключения к этой базе данных:
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 |
{ "ConnectionStrings": { "DefaultConnection": "Server=sql7991.site1now.net;Database=db_a88cd4_dbusers;User Id=db_a9932cd1_dbusers_admin;Password=f55fEDF256" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } |
6. Изменим класс Program для установки сервисов Entity Framework. В начале получаем строку подключения к базе данных, после добавляем класс ApplicationContext, в сервисы:
1 2 3 |
IConfigurationRoot _confString = new ConfigurationBuilder().SetBasePath(AppDomain.CurrentDomain.BaseDirectory).AddJsonFile("appsettings.json").Build(); builder.Services.AddDbContext<ApplicationContext>(options => options.UseSqlServer(_confString.GetConnectionString("DefaultConnection"))); |
7. Теперь добавим папку Interfaces и добавим туда новый интерфейс – IUsers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public interface IUsers { Task<IEnumerable<User>> GetAllUsersAsync(); Task<User> GetUserAsync(int id); Task AddUserAsync(User user); Task UpdateUserAsync(User user); Task DeleteUserAsync(User user); } |
8. Добавим папку Repository и туда новый класс UserRepository:
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
public class UserRepository : IUsers { private readonly ApplicationContext _context; public UserRepository(ApplicationContext context) { _context = context; } public async Task<IEnumerable<User>> GetAllUsersAsync() { return await _context.Users.ToListAsync(); } public async Task<User> GetUserAsync(int id) { return await _context.Users.FirstOrDefaultAsync(e => e.Id == id); } public async Task AddUserAsync(User user) { await _context.Users.AddAsync(user); await _context.SaveChangesAsync(); } public async Task DeleteUserAsync(User user) { _context.Users.Remove(user); await _context.SaveChangesAsync(); } public async Task UpdateUserAsync(User user) { _context.Users.Update(user); await _context.SaveChangesAsync(); } } |
9. В классе Program, добавим сервис IUsers:
1 |
builder.Services.AddTransient<IUsers, UserRepository>(); |
10. Сделаем начальное добавление данных, если они отсутствуют, реализуем добавление в отдельном классе, а не в ApplicationContext. Для этого в папке Models, создадим класс DbInit:
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 |
public class DbInit { public static void Init(ApplicationContext context) { if (!context.Users.Any()) { List<User> users = new List<User>() { new User { Name = "Alex", Age = 30}, new User { Name = "Bob", Age = 28}, new User { Name = "Marry", Age = 21}, new User { Name = "Tom", Age = 33}, new User { Name = "Kate", Age = 27} }; context.Users.AddRange(users); context.SaveChanges(); } } } |
11. Используем данный класс, в Program:
1 2 3 4 5 6 7 8 9 10 |
var app = builder.Build(); using (var scope = app.Services.CreateScope()) { ApplicationContext context = scope.ServiceProvider.GetRequiredService<ApplicationContext>(); DbInit.Init(context); } |
12. Добавим папку ViewModels, а в нее, новый класс UserViewModel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class UserViewModel { public int? Id { get; set; } [Required(ErrorMessage = "Name is required!")] public string? Name { get; set; } [Required(ErrorMessage = "Age is required!")] [Range(minimum: 1, maximum: 120)] public int Age { get; set; } } |
13. Создадим новый контроллер UsersController и добавим действия необходимые для управления пользователями:
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
public class UsersController : Controller { private readonly IUsers _users; public UsersController(IUsers users) { _users = users; } [HttpGet] public async Task<IActionResult> Index() { var allUsers = await _users.GetAllUsersAsync(); return View(allUsers); } [HttpPost] [AutoValidateAntiforgeryToken] public async Task<IActionResult> Delete(int userId) { var currentUser = await _users.GetUserAsync(userId); if (currentUser != null) { await _users.DeleteUserAsync(currentUser); } return RedirectToAction("Index", "Users"); } public IActionResult Create() { return View(); } [HttpPost] [AutoValidateAntiforgeryToken] public async Task<IActionResult> Create(UserViewModel userViewModel) { if (ModelState.IsValid) { await _users.AddUserAsync(new Models.User { Name = userViewModel.Name, Age = userViewModel.Age }); return RedirectToAction("Index", "Users"); } return View(userViewModel); } [HttpGet] public async Task<IActionResult> Edit(int userId) { var currentUser = await _users.GetUserAsync(userId); if (currentUser != null) { return View(new UserViewModel { Id = currentUser.Id, Name = currentUser.Name, Age = currentUser.Age }); } return NotFound(); } [HttpPost] [AutoValidateAntiforgeryToken] public async Task<IActionResult> Edit(UserViewModel userViewModel) { if (ModelState.IsValid) { await _users.UpdateUserAsync(new Models.User { Id = (int)userViewModel.Id!, Name = userViewModel.Name!, Age = userViewModel.Age }); return RedirectToAction("Index", "Users"); } return View(userViewModel); } } |
14. В папке View, добавим папку Users и в нее файл 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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
@model IEnumerable<User> @{ ViewBag.Title = "Users Manager"; } <div class="container px-4 py-5"> <h2 class="pb-2 border-bottom">Users Manager</h2> <div class="row g-4 py-5 row-cols-1"> <div> <a asp-controller="Users" asp-action="Create" class="btn btn-primary">Add new User</a> </div> <div class="p-5 mb-4 bg-light rounded-3 "> <table class="table"> <thead> <tr> <th>Id</th> <th>Name</th> <th>Age</th> <th></th> </tr> </thead> <tbody> @foreach (var user in @Model) { <tr> <td>@user.Id</td> <td>@user.Name</td> <td>@user.Age</td> <td> <div style="display:flex;"> <a class="btn btn-sm btn-primary" asp-controller="Users" asp-action="Edit" asp-route-userId="@user.Id" style="margin-right:5px;">Edit</a> <form asp-controller="Users" asp-action="Delete" asp-route-userId="@user.Id" method="post"> @Html.AntiForgeryToken() <button type="submit" class="btn btn-sm btn-danger">Delete</button> </form> </div> </td> </tr> } </tbody> </table> </div> </div> </div> |
15. В папку View — Users добавим файл Create.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 51 52 53 54 55 56 57 58 59 |
@using UsersApp.ViewModels @model UserViewModel @{ ViewBag.Title = "New User"; } <div class="container px-4 py-5" id="hanging-icons"> <h2 class="pb-2 border-bottom">New User</h2> <div class="row g-4 py-5 row-cols-1"> <div class="p-5 mb-4 bg-light rounded-3 "> <form asp-action="Create" asp-controller="Users"> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group"> <label asp-for="Name" class="control-label"></label> <input asp-for="Name" class="form-control" /> </div> <div class="form-group"> <label asp-for="Age" class="control-label"></label> <input asp-for="Age" class="form-control" /> </div><br /> <div class="form-group"> @Html.AntiForgeryToken() <input type="submit" value="Create" class="btn btn-primary" /> </div> </form> </div> </div> </div> |
16. В папку View — Users добавим файл Edit.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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
@using UsersApp.ViewModels @model UserViewModel @{ ViewBag.Title = "Edit User"; } <div class="container px-4 py-5"> <h2 class="pb-2 border-bottom">Edit User</h2> <div class="row g-4 py-5 row-cols-1"> <div class="p-5 mb-4 bg-light rounded-3 "> <form asp-action="Edit" asp-controller="Users"> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group"> <input type="hidden" asp-for="@Model.Id" /> </div> <div class="form-group"> <label asp-for="Name" class="control-label"></label> <input asp-for="Name" class="form-control" value="@Model.Name" /> </div> <div class="form-group"> <label asp-for="Age" class="control-label"></label> <input asp-for="Age" class="form-control" value="@Model.Age" /> </div><br /> <div class="form-group"> @Html.AntiForgeryToken() <input type="submit" value="Save" class="btn btn-primary" /> </div> </form> </div> </div> </div> |
17. Изменим файл представления _Layout.cshtml, добавим ссылку на страницу с пользователями:
1 2 3 4 5 |
<li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Users" asp-action="Index">Users</a> </li> |
Готово, теперь можно переходить к публикации проекта на хостинг!
Публикация проекта на хостинг
Как опубликовать приложение Asp.Net Core, рассмотрим на данном этапе
Проект полностью готов, он функционирует на локальном хосте и уже управляет данными из базы, размещенной на хостинге, поскольку на 4 шаге, мы создали базу на хостинге и в файле конфигурации указали подключение к этой базе данных.
- Прежде чем начать публикацию вашего проекта, убедимся, что сайт и домен работает.
- При публикации приложения, в папках где будут храниться картинки или файлы, всегда оставлять по одной картинке или файлу.
- Публикация проекта происходит через среду разработки, которая поддерживает Web Deploy, например, Visual Studio или WebMatrix. Мы рассмотрим процесс публикации веб-приложения через Visual Studio 2022. Открываем нужный проект и нажимаем правой клавишей по названию проекта – Publish.
- В выпадающем списке выберем пункт – FTP/FTPS Server.
- Укажем данные от хостинга, которые можно получить в письме или в настройках аккаунта провайдера:
В поле Site path, указываем папку, в которой размещается сайт, обычно это папка с названием домена.
6. Нажмем кнопку Finish. После этого попадем в главное окно публикации, в котором дополним по необходимости настройки:
Выберем пункт Show all settings и в появившемся окне по желанию меняем конфигурацию:
- Release: используется для сборки программы и её дальнейшего выпуска. Версия программы будет более оптимизирована по размеру и производительности и не будет содержать дополнительную информацию об отладке. Release обычно используется в качестве основной конфигурации по умолчанию.
- Debug: предназначена для отладки программы. Эта конфигурация отключает все настройки по оптимизации, включает информацию об отладке, что делает ваши программы больше и медленнее, но гораздо проще для проведения отладки.
- Target Framework: версия netcore, под которой компилируется локальное приложение.
- Deployment Mode: Framework-dependent
- Target Runtime: Portable
Если проект использует базу данных, дождитесь ее подключения, раскройте и просмотрите список ошибок, в данном случае указано, что не хватает библиотеки:
1 |
Microsoft.EntityFrameworkCore.Design |
Данная библиотека необходима для выполнения миграций в базе данных, для дальнейших публикаций, поэтому скачиваем ее и при настройках FTPProfile, мы сможем автоматически обновлять базу данных путем миграции (в данный момент галочку не ставим, таблицы создадутся за счет вызова метода Database.EnsureCreated() в классе ApplicationContext), об этом мы поговорим в следующей теме этой статьи:
7. Выполняем публикацию проекта на сервер, нажимаем вверху кнопку Publish и ждем полного развертывания проекта на сервере.
После публикации, проверяем сайт на хостинге.
Обновления базы данных
Для примера взято прошлое приложение с пользователями, настроено все, кроме базы данных.
- Определяем класс ApplicationContext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { } } |
- На сервере создаем новую базу данных и в файле appsettings.json прописываем подключение к ней:
1 2 3 4 5 |
"ConnectionStrings": { "DefaultConnection": "Server=sql8002.site4now.net;Database=db_a88cd4_dbusers;User Id=db_a88cd4_dbusers_admin;Password=leny1996" } |
- Чтобы на сервере создались все таблицы и настройки, необходимо выполнить миграцию. Для работы с миграциями, скачиваем библиотеку:
1 |
Install-Package Microsoft.EntityFrameworkCore.Tools |
Открываем Package Manager Console и вписываем команду:
1 |
Add-Migration Init |
В корне проекта, создастся папка с миграцией, выполним ее, используя команду:
1 |
Update-Database |
Если команда выполнена успешно, база данных на сервере должна обновиться. Запускаем проект и проверяем его работу.
- Теперь можно опубликовать проект на сервер, так как база данных уже наполнена всей необходимой информацией. Нажмем правой клавишей по названию проекта – Publish.
- Сайт опубликован, все работает, продолжаем писать сайт дальше. Добавляем новый класс Company и расширить класс User:
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 |
public class Company { public int Id { get; set; } public string Name { get; set; } public string? Address { get; set; } public virtual ICollection<User> Users { get; set; } } public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string? Hobby { get; set; } public int? CompanyId { get; set; } public Company? Company { get; set; } } |
Так же добавляем DbSet<Company> в класс ApplicationContext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public DbSet<Company> Companies { get; set; } public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { } } |
- Для изменения базы данных на сервере, выполним новую миграцию. Открываем Package Manager Console и вписываем команду:
1 |
Add-Migration Companies |
После этого создастся папка с миграцией, применяем ее, используя команду:
1 |
Update-Database |
Если команда выполнена успешно, база данных на сервере успешно обновиться.
- Изменим файлы проекта. В классе DbInit, добавим компании и привяжем пользователей к ним:
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 |
public class DbInit { public static void Init(ApplicationContext context) { if (!context.Users.Any()) { List<Company> companies = new List<Company>() { new Company { Name = "WallMart", Address = "Usa"}, new Company { Name = "Wendys", Address = "China"} }; List<User> users = new List<User>() { new User { Name = "Alex", Age = 30, Hobby = "Swim", Company = companies[0]}, new User { Name = "Bob", Age = 28}, new User { Name = "Marry", Age = 21, Hobby = "Chess"}, new User { Name = "Tom", Age = 33, Company = companies[1]}, new User { Name = "Kate", Age = 27, Company = companies[0]} }; context.Users.AddRange(users); context.SaveChanges(); } } } |
Так как пользователи уже есть, для начала, выполним удаление всех вручную, что бы они добавились снова, вместе с компаниями.
8. Теперь обновим репозиторий — UserRepository (добавим компании для пользователей):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public async Task<IEnumerable<User>> GetAllUsersAsync() { return await _context.Users.Include(e=>e.Company).ToListAsync(); } public async Task<User> GetUserAsync(int id) { return await _context.Users.Include(e => e.Company).FirstOrDefaultAsync(e => e.Id == id); } |
9. Обновим представление Index.cshtml, для вывода компаний пользователя. Так как у некоторых пользователей нет компании, необходимо проверить свойство Company на null, чтобы не получить ошибку (один из вариантов):
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<table class="table"> <thead> <tr> <th>Id</th> <th>Name</th> <th>Age</th> <th>Hobby</th> <th>Company</th> <th></th> </tr> </thead> <tbody> @foreach (var user in @Model) { <tr> <td>@user.Id</td> <td>@user.Name</td> <td>@user.Age</td> <td>@user.Hobby</td> <td> @if(user.Company is not null) { @user.Company.Name } </td> <td> <div style="display:flex;"> <a class="btn btn-sm btn-primary" asp-controller="Users" asp-action="Edit" asp-route-userId="@user.Id" style="margin-right:5px;">Edit</a> <form asp-controller="Users" asp-action="Delete" asp-route-userId="@user.Id" method="post"> @Html.AntiForgeryToken() <button type="submit" class="btn btn-sm btn-danger">Delete</button> </form> </div> </td> </tr> } </tbody> </table> |
- Запустим проект, все должно успешно работать:
Публикуем проект на сервер. По такому же принципу можно модифицировать проект и обновлять базу данных.
На этом статья «как опубликовать приложение Asp.Net Core», подошла к концу, надеюсь вам было интересно.
Поделитесь вашим опытом в комментариях, как вы публикуете Asp.Net Core приложение на хостинг?
Так же вам может быть интересна предыдущая статья — Как увеличить органический трафик на сайте.
Вы начинающий программист, который хочет изучить все тонкости языка C#?
Пройдите наш тест на 13 вопросов, чтобы узнать, как много вы знаете на самом деле!
C# Braincheck |
Вы хотите научится писать код на языке программирования C#?
Создавать различные информационные системы, состоящие из сайтов, мобильных клиентов, десктопных приложений, телеграмм-ботов и т.д.
Переходите к нам на страницу Dijix и ознакомьтесь с условиями обучения, мы специализируемся только на индивидуальных занятиях, как для начинающих, так и для более продвинутых программистов. Вы можете взять как одно занятие для проработки интересующего Вас вопроса, так и несколько, для более плотной работы. Благодаря личному кабинету, каждый студент повысит качество своего обучения, в вашем распоряжении:
- Доступ к пройденному материалу
- Тематические статьи
- Библиотека книг
- Онлайн тестирование
- Общение в закрытых группах
Живи в своем мире, программируй в нашем.