Полезные Функции EF Core. Окончание
Начало
3. Чистые SQL-запросы
EF Core 8 добавил новую функцию, которая позволяет нам запрашивать несопоставленные (unmapped) типы. Предположим, мы хотим получить данные из представления БД, хранимой процедуры или таблицы, которые напрямую не соответствуют ни одному из классов сущностей контекста БД. Например, получить сводные данные продаж продукта. В EF Core 8 можно определить простой класс и запросить его напрямую:
public class ProductSummary
{
public int Id { get; set; }
public string Name { get; set; }
public decimal TotalSales { get; set; }
}
var sums = await context.Database
.SqlQuery<ProductSummary>(
@$"""
SELECT p.Id, p.Name,
SUM(oi.Quantity * oi.UnitPrice) AS TotalSales
FROM Products p
JOIN OrderItems oi ON p.ProductId = oi.ProductId
WHERE p.CategoryId = {categoryId}
GROUP BY p.Id, p.Name
""")
.ToListAsync();
Метод SqlQuery возвращает IQueryable, что позволяет встраивать чистые SQL-запросы в LINQ-запросы.
Не забывайте использовать параметризованные запросы, чтобы предотвратить SQL-инъекции. Метод SqlQuery принимает FormattableString, что означает, что вы можете безопасно использовать интерполированную строку. Каждый аргумент преобразуется в параметр SQL.
См. подробнее.
4. Фильтры запросов
Фильтры автоматически добавляются в LINQ-запросы, когда вы извлекаете сущности соответствующего типа. Это избавляет от необходимости многократно писать одну и ту же логику фильтрации в нескольких местах.
Обычные сценарии:
- Мягкие удаления: отфильтровывают записи, помеченные как удалённые.
- Мультитенантность: фильтруют данные на основе текущего пользователя.
- Безопасность на уровне строк: ограничивают доступ к определённым записям на основе ролей или разрешений пользователей.
В мультитенантном приложении часто нужно фильтровать данные на основе текущего пользователя:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// Связываем продукты с пользователями
public int TenantId { get; set; }
}
protected override void
OnModelCreating(ModelBuilder mb)
{
// Текущий пользователь задаётся из контекста/запроса
mb.Entity<Product>()
.HasQueryFilter(p => p.TenantId == _currentTenantId);
}
// Запросы автоматически фильтруются по пользователю:
var products = context.Products.ToList();
См. подробнее о мультитенантности с помощью EF Core.
Настройка нескольких фильтров запроса для одной сущности применит только последний. Можно использовать IgnoreQueryFilters, чтобы обойти фильтры в запросах, где это необходимо.
Источник: https://www.milanjovanovic.tech/blog/5-ef-core-features-you-need-to-know