Вступление
В этом руководстве я продемонстрирую на своём примере, как вы можете безболезненно разместить ваш проект. Надеюсь, кому-то пригодится. Обязательно просмотрите блок с дополнительной информацией.
Технологии
.NET 6 WebAPI
Heroku
Entity Framework Core 6
-
PostgreSQL
Heroku
Регистрируемся на сервисе Heroku. Трудностей быть не должно.
![Регистрация в Heroku Регистрация в Heroku](https://habrastorage.org/getpro/habr/upload_files/252/3b9/b0e/2523b9b0e9d6811f606a94c58bebdcbc.png)
![Создаём новое приложение и указываем его название.
Создаём новое приложение и указываем его название.](https://habrastorage.org/getpro/habr/upload_files/ebb/348/89a/ebb34889a192051f3d1e0182e67123ec.png)
![Выбираем метод расположения через Github Выбираем метод расположения через Github](https://habrastorage.org/getpro/habr/upload_files/228/ced/09f/228ced09fc9e75ab39d52b97c93a5538.png)
После подключения к GitHub выбираем наш репозиторий с WebAPI, предварительно выгруженный туда. Также окном ниже выбираем ветвь с приложением, которое загрузится на сервис.
![Устанавливаем соединение с github Устанавливаем соединение с github](https://habrastorage.org/getpro/habr/upload_files/de3/2e6/c6a/de32e6c6acc2496cea0947db57a5184b.png)
![Выбираем ветвь репозитория Выбираем ветвь репозитория](https://habrastorage.org/getpro/habr/upload_files/0de/766/bb9/0de766bb955cbf61b5db326d1d8c3950.png)
А теперь важный аспект. Перед тем, как деплоить проект, его необходимо собрать.
Переходим во вкладку Settings и в пункте Buildpacks добавляем ссылку на "сборочный пакет" .NET 6.
![Buildpacks Buildpacks](https://habrastorage.org/getpro/habr/upload_files/c62/add/710/c62add710875d89e72e18cec8911e2b9.png)
Осталось следующее: установить аддон Heroku Postgres с помощью Configure Add-ons во вкладке Overview:
![Postgres add-on Postgres add-on](https://habrastorage.org/getpro/habr/upload_files/7af/16a/cb9/7af16acb958f4e3596f72ce7e13cc019.png)
Кликаем по аддону и переходим во вкладку Settings >> View Credentials, где находятся данные о нашей БД. Они нам понадобятся.
![Database Credentials Database Credentials](https://habrastorage.org/getpro/habr/upload_files/53a/dc8/cce/53adc8cce58ebfe2953307c530c54040.png)
Конфигурационные файлы
Переходим во вкладку Settings >> Config Vars и добавим конфигурационный файл под именем "ASPNETCORE_ENVIRONMENT"
. Это файл ключ-значение в котором будет удобно конфигурировать нашу рабочую среду между Development / Production
значениями.
![Config Vars Config Vars](https://habrastorage.org/getpro/habr/upload_files/e34/592/574/e34592574961fff8ef65429b450e2193.png)
Также здесь можно заметить такой файл как "DATABASE_URL"
. Это строка подключения к БД, предоставленная аддоном Heroku. Мы её будем использовать
Наш проект практически готов к публикации. Осталось его настроить.
Настройка проекта
Сразу скажу, что структура проекта, безусловно, может у каждого отличаться, но алгоритм действий будет одинаков при любом раскладе. Я продемонстрирую CRUD-проект. Условимся на том, что у вас объявлены соответствующие классы и сущности и осталось только настроить слой базы данных. Кратко ознакомимся.
Тема проекта: книжный магазин.
Приложение разделено на слои:
Domain (сущности)
Application (бизнес-логика)
Persistence (слой доступа к данным)
Presentation (web api)
![Структура проекта Структура проекта](https://habrastorage.org/getpro/habr/upload_files/6f3/89d/4a2/6f389d4a2dccb2744624b376de42b0fd.png)
Убедимся, что в сборке со слоем БД установлены все необходимые NuGet пакеты:
![Пакеты NuGet Пакеты NuGet](https://habrastorage.org/getpro/habr/upload_files/840/889/2f2/8408892f2c01e16a6aa131a25dfa0db4.png)
Если вы все делаете в одной сборке WebApi, этих пакетов будет достаточно.
Настройка контекста базы данных
Создадим интерфейс, который будет реализован контекстом BookStoreDbContext:
public interface IBookStoreDbContext
{
DbSet<Book> Books { get; set; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
Объявим необходимые таблицы и вызываем базовый конструктор. В этом же классе можно переопределить protected метод OnModelCreating(),
чтобы добавить начальные данные в таблицу, но это уже за рамками статьи.
public class BookStoreDbContext : DbContext, IBookStoreDbContext
{
public DbSet<Book> Books { get; set; }
public BookStoreDbContext(DbContextOptions<BookStoreDbContext> options)
: base(options) { }
}
Создадим класс DbInitializer
, который будет использоваться при старте приложения и методом Migrate()
создавать БД на основе нашего контекста, если её нет, а также программным способом накатывать миграции.
public class DbInitializer
{
public static void Initialize(BookStoreDbContext context)
{
context.Database.Migrate();
}
}
Переходим в файл конфигурации приложения appsettings.json
, где укажем строку подключения DbConnection
нашей локальной БД.
Вместо # - укажите ваши данные:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DbConnection": "Host=localhost;Database=#;Username=#;"
},
"AllowedHosts": "*"
}
Внедрение зависимостей
Добавим класс DependencyInjection
для нашего контекста. В нем мы внедрим наш контекст базы данных.
Создадим метод GetConnectionString(),
который получит данные о строке подключения из конфигурационного файла Heroku "DATABASE_URL"
.
Если в конфигурационном файле указана среда "Development"
(разработка), то мы используем нашу локальную строку подключения к БД в файле asppsettings.json
с её строкой подключения.
Подключим PostgreSql
.
public static class DependencyInjection
{
public static IServiceCollection AddPersistence(
this IServiceCollection services,
IConfiguration configuration)
{
var connectionString = string.Empty;
/* Проверим, в какой мы среде*/
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
{
connectionString = configuration["DbConnection"];
}
else
{
connectionString = GetConnectionString();
}
services.AddDbContext<BookStoreDbContext>(options =>
{
/* Подключаем PostgreSQL */
options.UseNpgsql(connectionString);
});
services.AddScoped<IBookStoreDbContext>(provider =>
provider.GetService<BookStoreDbContext>());
return services;
}
public static string GetConnectionString()
{
/* Используем строку подключения из конфигурацинного файла, обеспеченного Heroku*/
var connectionUrl = Environment.GetEnvironmentVariable("DATABASE_URL");
connectionUrl = connectionUrl.Replace("postgres://", string.Empty);
var userPassSide = connectionUrl.Split("@")[0];
var hostSide = connectionUrl.Split("@")[1];
var user = userPassSide.Split(":")[0];
var password = userPassSide.Split(":")[1];
var host = hostSide.Split("/")[0];
var database = hostSide.Split("/")[1].Split("?")[0];
return $"Host={host};Database={database};Username={user};Password={password};SSL Mode=Require;Trust Server Certificate=true";
}
В файле Program.cs
нашего WebAPI внедрим контекст БД и вызовем необходимый метод Initialize()
:
using (var scope = app.Services.CreateScope())
{
try
{
var context = scope.ServiceProvider.GetRequiredService<BookStoreDbContext>();
DbInitializer.Initialize(context);
}
catch (Exception)
{
}
}
Добавим миграцию в проект с помощью команды add-migration Init:
![Package Manager Console Package Manager Console](https://habrastorage.org/getpro/habr/upload_files/d4b/e14/d28/d4be14d281ca02237b92013d2a84aa75.png)
Деплоим проект!
Вернёмся в Heroku и нажмём заветную кнопку Deploy Branch:
![Manual Deploy Manual Deploy](https://habrastorage.org/getpro/habr/upload_files/370/b37/b90/370b37b90d67c6a9db389aa68530c3a1.png)
Заключение
Поздравляю! Можно переходить на сайт. Вы можете наблюдать за состоянием сборки вашего приложения во вкладке Activity.
В моём случае это получение информации о книгах.
![API response API response](https://habrastorage.org/getpro/habr/upload_files/4d7/d64/310/4d7d64310c39c21c1f5e3e01ffcf5f47.png)
Я надеюсь, что изложил максимально просто и понятно. Если исключить БД, то всего лишь необходимо подключить .NET buildpack и деплоить проект. Проблем быть не должно.
Всех благ!
Дополнительная информация
Heroku:
Ролик, который вдохновил меня на написание статьи:
https://www.youtube.com/watch?v=BqI1hu0gIb0&ab_channel=CoderFoundry
Аналогичная статья с размещением API вместе с Docker:
https://www.c-sharpcorner.com/article/deploy-a-net-api-to-heroku-through-github-actions/
Создание контекста БД и подключение PostgreSQL с помощью Entity Framework Core:
На всякий случай добавлю .NET Buildpack для размещения вашего приложения:
Более подробное описание добавления Entity Framework Core в проект:
Комментарии (5)
thedecadence
25.10.2022 09:25+4Хочу сделать поправку по самому началу "Регистрируемся на сервисе Heroku. Трудностей быть не должно".
Начиная с марта этого года для регистрация на Хероку для представителей РФ стала невозможной. Кроме того, для авторизации в личном кабинете и доступу к большинству бесплатных сервисов (и платных тоже, естественно) нужна привязка банковской карты, но карты российских банков и виртуальные карты хероку не принимает.
Также с 20 ноября хероку отменяет все бесплатные сервисы.
И ещё - у многих российских провайдеров имеются проблемы с доступом в личный кабинет хероку - приходится пользоваться vpn.
Поэтому я перевёл многие проекты на российские сервисы - благо их много, и они очень дешёвые по сравнению с зарубежными аналогами.
Сама статья интересная, многое применимо и к другим сервисам, похожими на Хероку, но её актуальность в данный момент имеется скорее для зарубежных разработчиков.
Godi Автор
25.10.2022 13:24Спасибо за комментарий.
Я использую российский провайдер, и с регистрацией проблем действительно не возникало. Не могли бы ли вы предоставить источник, в котором указано, что Heroku стал недоступным для пользователей РФ?
Что касается привязки банковской карты для использования сервисов - не уверен.
А с переходом на платную основу и вправду печально :с
thedecadence
25.10.2022 18:22+1Источник - сам Хероку. С апреля зарегистрироваться как пользователю РФ не даёт - вылезает соответствующее сообщение.
Mayhem924
25.10.2022 14:34+2Добавлю немного насчет метода Initialize
Вызывать миграцию программно таким образом неправильно:
context.Database.EnsureCreated(); context.Database.Migrate();
Пояснение из документации Microsoft
Не вызывайте EnsureCreated() перед Migrate(). EnsureCreated() обходит миграции, чтобы создать схему, что приводит к сбою Migrate().
thedecadence
Хочу сделать поправку по самому началу "Регистрируемся на сервисе Heroku. Трудностей быть не должно".
Начиная с марта этого года для регистрация на Хероку для представителей РФ стала невозможной. Кроме того, для авторизации в личном кабинете и доступу к большинству бесплатных сервисов (и платных тоже, естественно) нужна привязка банковской карты, но карты российских банков и виртуальные карты хероку не принимает.
Также с 20 ноября хероку отменяет все бесплатные сервисы.
И ещё - у многих российских провайдеров имеются проблемы с доступом в личный кабинет хероку - приходится пользоваться vpn.
Поэтому я перевёл многие проекты на российские сервисы - благо их много, и они очень дешёвые по сравнению с зарубежными аналогами.
Сама статья интересная, многое применимо и к другим сервисам, похожими на Хероку, но её актуальность в данный момент имеется скорее для зарубежных разработчиков.