С какой ORM работаешь для взаимодействия с базой данных?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Предпочтительные ORM: Entity Framework Core и Dapper
Как senior C# backend-разработчик с фокусом на высоконагруженные enterprise-системы, я использую преимущественно Entity Framework Core (EF Core) как основную ORM, но в критически важных по производительности сценариях дополняю её Dapper — это гибридный подход, который стал отраслевым стандартом для баланса между удобством и скоростью.
Почему Entity Framework Core — основной выбор
EF Core — это развитая, полнофункциональная ORM Microsoft, которая предоставляет несколько ключевых преимуществ:
1. LINQ (Language Integrated Query) как основной API:
// Декларативные запросы, проверяемые на этапе компиляции
var activeOrders = await context.Orders
.Include(o => o.Customer)
.Where(o => o.Status == OrderStatus.Active && o.CreatedDate > DateTime.UtcNow.AddDays(-7))
.Select(o => new OrderDto
{
Id = o.Id,
CustomerName = o.Customer.FullName,
TotalAmount = o.Total
})
.ToListAsync();
LINQ позволяет писать выразительные запросы, которые компилируются в SQL, обеспечивая безопасность типов и предотвращая многие SQL-инъекции.
2. Code-First и миграции:
// Определение модели в коде
public class Order
{
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
public decimal Total { get; set; }
// Навигационные свойства
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
// Конфигурация Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasIndex(o => o.CustomerId)
.HasName("IX_Orders_CustomerId");
modelBuilder.Entity<Order>()
.Property(o => o.Total)
.HasPrecision(18, 2);
}
Автоматические миграции (Add-Migration, Update-Database) синхронизируют схему БД с моделями, что критично в Agile-разработке.
3. Трекер изменений и Unit of Work: EF Core автоматически отслеживает изменения сущностей, что упрощает обновления:
var order = await context.Orders.FindAsync(orderId);
order.Status = OrderStatus.Completed; // EF Core отследит изменение
order.CompletedDate = DateTime.UtcNow;
await context.SaveChangesAsync(); // Все изменения в одной транзакции
4. Поддержка сложных сценариев:
- Генерация и выполнение сырых SQL через
FromSqlRaw()иExecuteSqlRaw() - Глобальные фильтры запросов (например, мягкое удаление)
- Загрузка связанных данных: Eager, Explicit, Lazy и Select loading
- Конкурентный контроль через Tokens или RowVersion
Когда и почему добавляется Dapper
Несмотря на преимущества EF Core, в высоконагруженных сценариях я использую Dapper — микро-ORM от создателей Stack Overflow:
1. Производительность сложных запросов:
// Dapper для отчетов и аналитики
var sql = @"
SELECT
c.Id, c.FullName,
o.Id, o.Total, o.CreatedDate
FROM Customers c
JOIN Orders o ON c.Id = o.CustomerId
WHERE o.CreatedDate > @startDate
ORDER BY o.Total DESC";
using var connection = new SqlConnection(connectionString);
var results = await connection.QueryAsync<Customer, Order, Customer>(
sql,
(customer, order) =>
{
customer.Orders ??= new List<Order>();
customer.Orders.Add(order);
return customer;
},
new { startDate = DateTime.UtcNow.AddMonths(-1) },
splitOn: "Id"
);
Dapper в 5-10 раз быстрее EF Core на сложных JOIN-запросах, так как минимизирует накладные расходы.
2. Прямой контроль над SQL:
- Можно использовать все возможности конкретной СУБД (оконные функции, CTE, специфичные оптимизации)
- Нет "магии" EF Core — полная прозрачность выполняемого SQL
- Легче оптимизировать запросы совместно с DBA
Архитектурный подход: репозитории и спецификации
Для абстрагирования ORM я реализую паттерн Repository с Specification Pattern:
public interface IRepository<T> where T : BaseEntity
{
Task<T?> GetByIdAsync(int id);
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);
Task<T?> FirstOrDefaultAsync(ISpecification<T> spec);
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
}
// Спецификация для фильтрации и инклудов
public class ActiveOrdersSpecification : BaseSpecification<Order>
{
public ActiveOrdersSpecification(int customerId)
: base(o => o.CustomerId == customerId && o.Status == OrderStatus.Active)
{
AddInclude(o => o.Customer);
AddInclude(o => o.OrderItems);
ApplyOrderByDescending(o => o.CreatedDate);
}
}
Дополнительные инструменты и практики
- LINQKit для динамического построения предикатов
- EF Core Plus (Z.EntityFramework.Plus) для batch-операций
- Scaffold-DbContext для Database-First подхода к легаси системам
- Профилирование через MiniProfiler или Application Insights
- Использование ReadOnlyContext для запросов только на чтение
- Реализация Retry Policies через Polly для устойчивости
Выбор СУБД и особенности
Подход варьируется в зависимости от СУБД:
- SQL Server: Полная поддержка EF Core + Dapper
- PostgreSQL: Npgsql + EF Core с JSON-поддержкой
- MySQL: Pomelo.EntityFrameworkCore.MySql с осторожностью к миграциям
Этот гибридный подход позволяет мне использовать удобство и безопасность EF Core для 80% операций CRUD и бизнес-логики, сохраняя возможность использовать производительность Dapper для сложных запросов и отчетов, что оптимально для современных высоконагруженных приложений.