Skip to main content
Entity Framework Core (EF Core) — это ORM для .NET, а Npgsql — его провайдер для PostgreSQL. В этом руководстве вы разработаете минимальный API на ASP.NET Core, создадите для него таблицу с помощью миграции EF Core и реализуете конечные точки для создания, чтения, обновления и удаления данных. Все соединения используют TLS с полной проверкой сертификата.

Предварительные требования

  • .NET SDK 10. Это руководство проверено на .NET SDK 10.0.401, EF Core 10.0.12 и Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 (Npgsql 10.0.3).
  • Аккаунт ClickHouse Cloud
  • psql для создания базы данных. Оператор CREATE DATABASE также можно выполнить в консоли SQL.

Создание сервиса ClickHouse Managed Postgres

В консоли ClickHouse Cloud нажмите New service и выберите Postgres. Инстанс будет готов через несколько минут. Пошаговая инструкция приведена в кратком руководстве по началу работы.

Получите сведения о подключении

Нажмите Подключение на левой боковой панели вашего сервиса. Оставьте выбранным вариант Directly, включите Use SSL и скопируйте пароль и имя хоста сервера. Затем нажмите Download CA certificate. Сертификат можно скачать и позже в разделе Settings → CA Certificate. Он уникален для вашего инстанса, поэтому Npgsql может с его помощью убедиться, что подключается именно к вашему серверу. В этом руководстве и приложение, и миграции EF Core подключаются к Postgres напрямую через порт 5432. Приложение ASP.NET Core работает как долгоживущий процесс, а Npgsql уже обеспечивает в нём пул соединений (по умолчанию до 100 на одну строку подключения), поэтому дополнительный пулер не нужен. Если вы запускаете много инстансов приложения, см. раздел Использование PgBouncer. В модальном окне Подключение нет вкладки для .NET, а Npgsql не принимает URL вида postgresql:// из вкладки url: вместо этого он использует строки подключения формата Keyword=Value. Возьмите значения из URL и сопоставьте их с ключевыми словами Npgsql:

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

Создайте проект ASP.NET Core, добавьте провайдер Npgsql для EF Core и пакет EF Core Design, необходимый на этапе разработки, а затем установите инструмент командной строки dotnet ef:
Добавляете в существующее приложение?Пропустите dotnet new web. Если ваше приложение использует EF Core с SQLite или другим провайдером:
  • Выполните dotnet remove package Microsoft.EntityFrameworkCore.Sqlite и dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL. Также установите Microsoft.EntityFrameworkCore.Design и инструмент dotnet-ef, если они ещё не установлены.
  • В Program.cs замените UseSqlite на UseNpgsql и удалите строку подключения SQLite из appsettings.json.
  • Миграции привязаны к конкретному провайдеру, поэтому удалите папку Migrations. Новую начальную миграцию вы создадите в разделе Создание схемы.
Затем переходите к разделу Настройка соединения, указав вместо TodoDb имя строки подключения вашего приложения, а раздел Определение модели пропустите.
В macOS включите TLS 1.3ClickHouse Managed Postgres принимает только TLS 1.3. По умолчанию .NET в macOS не поддерживает TLS 1.3, поэтому любое соединение завершается ошибкой Exception while performing SSL handshake с внутренней ошибкой bad protocol version. В .NET 10 вместо этого можно использовать Apple Network.framework, который поддерживает TLS 1.3. Добавьте следующее в элемент <Project> файла TodoApi.csproj:
Этот параметр действует как для приложения, так и для dotnet ef, и ни на что не влияет в Linux и Windows. Вместо этого можно задать переменную окружения DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=1.

Настройка соединения

Переместите загруженный CA‑сертификат в каталог проекта и переименуйте его в ca-certificate.pem. Создайте базу данных для приложения. Замените <PASSWORD> и хост на значения из модального окна Подключение:
Сохраните строку подключения с помощью Secret Manager, чтобы пароль не попал ни в appsettings.json, ни в систему контроля версий:
ASP.NET Core считывает пользовательские секреты в окружении Development, которое dotnet run и dotnet ef используют по умолчанию. В продакшн-окружении вместо этого задайте переменную окружения ConnectionStrings__TodoDb. С SSL Mode=VerifyFull Npgsql проверяет, что сертификат сервера подписан CA из Root Certificate и соответствует имени хоста. Относительный путь в Root Certificate Npgsql отсчитывает от текущего рабочего каталога — при запуске через dotnet run и dotnet ef это каталог проекта. Если вы запускаете приложение из другого места, укажите абсолютный путь.
Если CA указан неверно или отсутствует либо хост не соответствует сертификату (например, при подключении по IP-адресу), соединение завершается ошибкой Exception while performing SSL handshake с вложенной ошибкой The remote certificate was rejected by the provided RemoteCertificateValidationCallback. Если файла Root Certificate не существует, вложенная ошибка будет Could not find file. Также учтите следующее:
  • По умолчанию Npgsql использует SSL Mode=Prefer, а SSL Mode=Require тоже не проверяет сертификат, даже если задан Root Certificate. Всегда указывайте SSL Mode=VerifyFull.
  • Npgsql считывает переменные окружения PGSSLROOTCERT и PGPASSWORD, но не PGHOST и PGSSLMODE. Поэтому одних только переменных с вкладки env в окне Подключение недостаточно.

Определение модели

Создайте файл Todo.cs с классом сущности:
Todo.cs
Создайте файл TodoDb.cs с классом DbContext:
TodoDb.cs
Замените содержимое Program.cs. Этот код регистрирует TodoDb с провайдером Npgsql и сопоставляет каждой операции отдельную конечную точку. В существующем приложении достаточно лишь вызова AddDbContext с UseNpgsql:
Program.cs
Конечная точка /db/tls считывает данные из pg_stat_ssl для собственного сеанса приложения с базой данных — так можно убедиться, что соединение зашифровано.

Создание схемы

Сгенерируйте миграцию на основе модели и примените её:
dotnet ef migrations add записывает миграцию в папку Migrations, а dotnet ef database update применяет её и фиксирует в таблице __EFMigrationsHistory. При первом запуске EF Core ещё до создания этой таблицы записывает в журнал ошибку SELECT из __EFMigrationsHistory — её можно игнорировать. Перед применением миграций EF Core устанавливает блокировку, чтобы два развертывания не могли одновременно выполнять миграцию одной и той же базы данных. В Npgsql роль такой блокировки выполняет LOCK TABLE для __EFMigrationsHistory внутри транзакции миграции, а не рекомендательная (advisory) блокировка уровня сеанса, поэтому она снимается по завершении транзакции.

Запуск и проверка

Запустите приложение на порту 3115:
Во втором терминале создайте две задачи, отметьте первую как выполненную и удалите вторую:
Запрос DELETE возвращает 204 No Content, поэтому его строка пуста. Убедитесь, что сеанс приложения с базой данных использует TLS:
Откройте http://localhost:3115/todos в браузере, чтобы увидеть список оставшихся задач:
Чтобы просмотреть данные в консоли, откройте консоль SQL на левой боковой панели вашего сервиса, разверните guide_dotnet, затем public и выберите таблицу Todos. EF Core использует имена классов и свойств в качестве идентификаторов в кавычках, поэтому таблица называется "Todos", а столбцы — "Id", "Title" и т. д. В таблице осталась одна строка:

Использование PgBouncer

Npgsql открывает до 100 соединений на каждый инстанс приложения (Maximum Pool Size). Если суммарное число соединений от множества инстансов приближается к лимиту соединений Postgres, подключайте приложение через встроенный PgBouncer. Выберите via PgBouncer в модальном окне Подключение и укажите в строке подключения порт 6432:
Подойдёт тот же CA‑сертификат. PgBouncer работает в режиме пулинга транзакций, поэтому каждая транзакция может выполняться через отдельное соединение с Postgres. При настройках EF Core и Npgsql по умолчанию больше ничего менять не нужно:
  • Подготовленные операторы: EF Core не подготавливает операторы, а автоматическая подготовка в Npgsql по умолчанию отключена (Max Auto Prepare=0), поэтому запросы выполняются через безымянные операторы, которые работают с PgBouncer. Не меняйте эту конфигурацию. При работе через PgBouncer подготовленные операторы, созданные с помощью Max Auto Prepare или NpgsqlCommand.Prepare, время от времени завершаются ошибкой 08P01: prepared statement did not exist (её предотвращает No Reset On Close=true), а после миграции, изменившей тип столбца, они продолжают завершаться ошибкой cached plan must not change result type даже на новых соединениях. Если вам нужны подготовленные операторы, подключайтесь напрямую.
  • Состояние сеанса: настройки, заданные с помощью SET, включая search_path, не сохраняются между транзакциями. Вместо этого используйте SET LOCAL внутри транзакции. PgBouncer отклоняет ключевые слова строки подключения Search Path и Options с ошибкой unsupported startup parameter, а Timezone и Application Name работают.
  • Миграции: dotnet ef database update также работает через PgBouncer, поскольку блокировка миграции — это блокировка таблицы, которая снимается по завершении её транзакции.
  • Проверка TLS: pg_stat_ssl описывает соединение между PgBouncer и Postgres, поэтому /db/tls возвращает none. При этом соединение вашего приложения с PgBouncer по-прежнему проверяется: с неверным CA‑сертификатом подключение завершится такой же ошибкой.

Дальнейшие шаги

Последнее изменение 14 октября 2026 г.