← Назад к вопросам

С какой ORM работаешь для взаимодействия с базой данных?

1.3 Junior🔥 212 комментариев
#Entity Framework и ORM#Базы данных и SQL

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Предпочтительные 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 для сложных запросов и отчетов, что оптимально для современных высоконагруженных приложений.

С какой ORM работаешь для взаимодействия с базой данных? | PrepBro