Ответ
В нескольких production-проектах я использовал PostgreSQL как основную реляционную БД благодаря ее надежности, богатому функционалу и открытой лицензии. Работа строилась через Npgsql — официальный и высокопроизводительный ADO.NET-провайдер.
1. Подключение и конфигурация
Устанавливается пакет Npgsql.EntityFrameworkCore.PostgreSQL для EF Core.
// В Startup.cs / Program.cs
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))
// Важная оптимизация для PostgreSQL
.UseSnakeCaseNamingConvention() // Опционально: snake_case в БД
);
Строка подключения в appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=myapp;Username=postgres;Password=secret"
}
2. Работа с продвинутыми типами данных PostgreSQL
-
JSONB: Хранение и запросы к полуструктурированным данным.
// Модель EF Core public class Product { public int Id { get; set; } // Словарь метаданных хранится как JSONB public Dictionary<string, object> Metadata { get; set; } = new(); } // В DbContext modelBuilder.Entity<Product>() .Property(p => p.Metadata) .HasColumnType("jsonb"); // Запрос с фильтрацией по JSONB полю var products = await dbContext.Products .Where(p => p.Metadata["color"].ToString() == "red") .ToListAsync(); - Массивы и составные типы: Полезны для специализированных сценариев.
3. Выполнение сырых SQL-запросов Для сложных отчетов или оптимизированных запросов.
using var connection = new NpgsqlConnection(connectionString);
var sql = @"
SELECT u.name, COUNT(o.id) as order_count
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.created_at > @cutoffDate
GROUP BY u.id
HAVING COUNT(o.id) > @minOrders";
var results = await connection.QueryAsync<UserStats>(sql, new {
cutoffDate = DateTime.UtcNow.AddMonths(-1),
minOrders = 5
});
// Используется Dapper для простого маппинга, но можно и через DataReader.
4. Ключевые практики
- Индексы: Активно использовал GIN-индексы для JSONB и полнотекстового поиска, BRIN для временных рядов.
- Конкурентность: Использование
FOR UPDATE SKIP LOCKEDдля реализации надежных очередей задач. - Репликация: Настройка чтения из реплик для распределения нагрузки в веб-приложениях.
Ответ 18+ 🔞
Да ты послушай, как я с этой PostgreSQL работал на продакшене — просто песня, а не база данных! Ну реально, берёшь её как основную реляционку, и спишь спокойно: надёжная, как швейцарские часы, функционала — овердохуища, да ещё и лицензия открытая, не надо никому мозги выносить. А через Npgsql с ней общаешься — это официальный провайдер для .NET, и работает, как часы, без всяких костылей.
1. Подключиться — раз плюнуть
Ставишь пакет Npgsql.EntityFrameworkCore.PostgreSQL для EF Core и вперёд.
// В Startup.cs / Program.cs
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))
// Важная оптимизация для PostgreSQL
.UseSnakeCaseNamingConvention() // Опционально: snake_case в БД
);
А в appsettings.json строку подключения пишешь, как в аптеке:
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=myapp;Username=postgres;Password=secret"
}
2. А вот тут начинается магия — продвинутые типы данных
-
JSONB: Это ж просто мечта для всякой полуструктурированной хуйни! Засунул словарь — и живи спокойно.
// Модель EF Core public class Product { public int Id { get; set; } // Словарь метаданных хранится как JSONB public Dictionary<string, object> Metadata { get; set; } = new(); } // В DbContext modelBuilder.Entity<Product>() .Property(p => p.Metadata) .HasColumnType("jsonb"); // И запросы к этому полю делаешь, как к обычному! var products = await dbContext.Products .Where(p => p.Metadata["color"].ToString() == "red") .ToListAsync(); - Массивы и составные типы: Для особо извращённых сценариев — самое то, но без фанатизма.
3. Когда EF Core не справляется — пускаем в ход сырые SQL-запросы Бывает, отчёты такие замороченные, что проще написать запрос вручную, чем городить LINQ-овод.
using var connection = new NpgsqlConnection(connectionString);
var sql = @"
SELECT u.name, COUNT(o.id) as order_count
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.created_at > @cutoffDate
GROUP BY u.id
HAVING COUNT(o.id) > @minOrders";
var results = await connection.QueryAsync<UserStats>(sql, new {
cutoffDate = DateTime.UtcNow.AddMonths(-1),
minOrders = 5
});
// Тут я Dapper прикрутил для маппинга — удобно, но можно и через DataReader, если мазохист.
4. Без этих практик — никуда, запомни как "Отче наш"
- Индексы: GIN-индексы для JSONB и полнотекстового поиска — просто огонь. А для временных рядов BRIN — экономия места и скорости, как будто тебе скидку сделали.
- Конкурентность: Хочешь очередь задач сделать, чтобы не гонялись за одной записью?
FOR UPDATE SKIP LOCKED— твой лучший друг, без него просто пиздец наступает. - Репликация: Чтение с реплик настраиваешь — и нагрузка распределяется, как масло по бутерброду. Главное, чтобы реплики не отставали, а то будет тебе хиросима в базе данных.
Вот так вот, коротко и без воды. PostgreSQL — это сила, а с Npgsql она становится твоей силой. Главное — не накосячить с транзакциями, а то потом будешь локти кусать.