Вступление

В этом руководстве я продемонстрирую на своём примере, как вы можете безболезненно разместить ваш проект. Надеюсь, кому-то пригодится. Обязательно просмотрите блок с дополнительной информацией.

Технологии

  • .NET 6 WebAPI

  • Heroku

  • Entity Framework Core 6

  • PostgreSQL

Heroku

Регистрируемся на сервисе Heroku. Трудностей быть не должно.

Регистрация в Heroku
Регистрация в Heroku
Создаём новое приложение и указываем его название.
Создаём новое приложение и указываем его название.
Выбираем метод расположения через Github
Выбираем метод расположения через Github

После подключения к GitHub выбираем наш репозиторий с WebAPI, предварительно выгруженный туда. Также окном ниже выбираем ветвь с приложением, которое загрузится на сервис.

Устанавливаем соединение с github
Устанавливаем соединение с github
Выбираем ветвь репозитория
Выбираем ветвь репозитория

А теперь важный аспект. Перед тем, как деплоить проект, его необходимо собрать.

Переходим во вкладку Settings и в пункте Buildpacks добавляем ссылку на "сборочный пакет" .NET 6.

Buildpacks
Buildpacks

https://github.com/jincod/dotnetcore-buildpack

Осталось следующее: установить аддон Heroku Postgres с помощью Configure Add-ons во вкладке Overview:

Postgres add-on
Postgres add-on

Кликаем по аддону и переходим во вкладку Settings >> View Credentials, где находятся данные о нашей БД. Они нам понадобятся.

Database Credentials
Database Credentials

Конфигурационные файлы

Переходим во вкладку Settings >> Config Vars и добавим конфигурационный файл под именем "ASPNETCORE_ENVIRONMENT". Это файл ключ-значение в котором будет удобно конфигурировать нашу рабочую среду между Development / Production значениями.

Config Vars
Config Vars

Также здесь можно заметить такой файл как "DATABASE_URL". Это строка подключения к БД, предоставленная аддоном Heroku. Мы её будем использовать

Наш проект практически готов к публикации. Осталось его настроить.

Настройка проекта

Сразу скажу, что структура проекта, безусловно, может у каждого отличаться, но алгоритм действий будет одинаков при любом раскладе. Я продемонстрирую CRUD-проект. Условимся на том, что у вас объявлены соответствующие классы и сущности и осталось только настроить слой базы данных. Кратко ознакомимся.

Тема проекта: книжный магазин.

Приложение разделено на слои:

  • Domain (сущности)

  • Application (бизнес-логика)

  • Persistence (слой доступа к данным)

  • Presentation (web api)

Структура проекта
Структура проекта

 Убедимся, что в сборке со слоем БД установлены все необходимые NuGet пакеты:

Пакеты NuGet
Пакеты NuGet

Если вы все делаете в одной сборке 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

Деплоим проект!

Вернёмся в Heroku и нажмём заветную кнопку Deploy Branch:

Manual Deploy
Manual Deploy

Заключение

Поздравляю! Можно переходить на сайт. Вы можете наблюдать за состоянием сборки вашего приложения во вкладке Activity.

В моём случае это получение информации о книгах.

API response
API response

Я надеюсь, что изложил максимально просто и понятно. Если исключить БД, то всего лишь необходимо подключить .NET buildpack и деплоить проект. Проблем быть не должно.

Всех благ!

Дополнительная информация

Heroku:

https://dashboard.heroku.com/

Ролик, который вдохновил меня на написание статьи:

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:

https://www.npgsql.org/efcore/

На всякий случай добавлю .NET Buildpack для размещения вашего приложения:

https://github.com/jincod/dotnetcore-buildpack

Более подробное описание добавления Entity Framework Core в проект:

https://www.youtube.com/watch?v=wkF3csnu2hE&list=PLEtg-LdqEKXbpq4RtUp1hxZ6ByGjnvQs4&index=3&ab_channel=PlatinumDEV

Комментарии (5)


  1. thedecadence
    25.10.2022 09:25
    +4

    Хочу сделать поправку по самому началу "Регистрируемся на сервисе Heroku. Трудностей быть не должно".

    Начиная с марта этого года для регистрация на Хероку для представителей РФ стала невозможной. Кроме того, для авторизации в личном кабинете и доступу к большинству бесплатных сервисов (и платных тоже, естественно) нужна привязка банковской карты, но карты российских банков и виртуальные карты хероку не принимает.

    Также с 20 ноября хероку отменяет все бесплатные сервисы.

    И ещё - у многих российских провайдеров имеются проблемы с доступом в личный кабинет хероку - приходится пользоваться vpn.

    Поэтому я перевёл многие проекты на российские сервисы - благо их много, и они очень дешёвые по сравнению с зарубежными аналогами.

    Сама статья интересная, многое применимо и к другим сервисам, похожими на Хероку, но её актуальность в данный момент имеется скорее для зарубежных разработчиков.


  1. thedecadence
    25.10.2022 09:25
    +4

    Хочу сделать поправку по самому началу "Регистрируемся на сервисе Heroku. Трудностей быть не должно".

    Начиная с марта этого года для регистрация на Хероку для представителей РФ стала невозможной. Кроме того, для авторизации в личном кабинете и доступу к большинству бесплатных сервисов (и платных тоже, естественно) нужна привязка банковской карты, но карты российских банков и виртуальные карты хероку не принимает.

    Также с 20 ноября хероку отменяет все бесплатные сервисы.

    И ещё - у многих российских провайдеров имеются проблемы с доступом в личный кабинет хероку - приходится пользоваться vpn.

    Поэтому я перевёл многие проекты на российские сервисы - благо их много, и они очень дешёвые по сравнению с зарубежными аналогами.

    Сама статья интересная, многое применимо и к другим сервисам, похожими на Хероку, но её актуальность в данный момент имеется скорее для зарубежных разработчиков.


  1. Godi Автор
    25.10.2022 13:24

    Спасибо за комментарий.

    Я использую российский провайдер, и с регистрацией проблем действительно не возникало. Не могли бы ли вы предоставить источник, в котором указано, что Heroku стал недоступным для пользователей РФ?

    Что касается привязки банковской карты для использования сервисов - не уверен.

    А с переходом на платную основу и вправду печально :с


    1. thedecadence
      25.10.2022 18:22
      +1

      Источник - сам Хероку. С апреля зарегистрироваться как пользователю РФ не даёт - вылезает соответствующее сообщение.


  1. Mayhem924
    25.10.2022 14:34
    +2

    Добавлю немного насчет метода Initialize

    Вызывать миграцию программно таким образом неправильно:

    context.Database.EnsureCreated();
    context.Database.Migrate();  
    

    Пояснение из документации Microsoft

    Не вызывайте EnsureCreated() перед Migrate(). EnsureCreated() обходит миграции, чтобы создать схему, что приводит к сбою Migrate().


    1. Godi Автор
      25.10.2022 14:35

      Спасибо, исправил.