0Pricing
C# Academy · Урок

Сырой SQL, хранимые процедуры и интерполяция

Безопасно выполняйте сырой SQL с помощью FromSqlRaw, FromSqlInterpolated и хранимых процедур, избегая внедрения кода.

«Сырой SQL, хранимые процедуры и интерполяция» — бесплатный урок C# Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Когда использовать сырой SQL в EF Core

Большинство запросов хорошо работает с LINQ, но для некоторых сценариев нужен сырой SQL: сложная аналитика, функции конкретного поставщика, хранимые процедуры или критичные к производительности запросы, которые LINQ переводит неудачно.

EF Core предоставляет безопасные способы использования сырого SQL без ущерба для безопасности.

FromSqlRaw: безопасные параметризованные запросы

FromSqlRaw выполняет сырой SQL и преобразует результаты обратно в сущности. Всегда используйте заполнители {0} — никогда не объединяйте строки — чтобы предотвратить SQL-инъекции.

// SAFE: parameterized
var products = await _db.Products
    .FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 100m)
    .ToListAsync();

// DANGEROUS: never do this
// FromSqlRaw("SELECT * FROM Products WHERE Price > " + price)

FromSqlInterpolated: безопасность интерполированных строк

FromSqlInterpolated принимает интерполированную строку C#. EF Core автоматически преобразует интерполированные значения в параметры SQL — это безопасно и удобно для чтения.

decimal minPrice = 100m;
string category = "Electronics";

var products = await _db.Products
    .FromSqlInterpolated(
        $"SELECT * FROM Products WHERE Price > {minPrice} AND Category = {category}")
    .OrderBy(p => p.Name)
    .ToListAsync();
// Values become @p0, @p1 parameters automatically

Составление запросов LINQ после FromSql

После FromSql можно объединять операторы LINQ в цепочку. EF Core оборачивает сырой SQL во вложенный запрос и применяет поверх него LINQ, что удобно для добавления фильтрации или разбиения на страницы.

decimal min = 50m;
var page = await _db.Products
    .FromSqlInterpolated($"SELECT * FROM Products WHERE Price > {min}")
    .Where(p => p.IsActive)        // additional LINQ filter
    .OrderBy(p => p.Price)
    .Skip(0).Take(20)              // pagination
    .ToListAsync();
// Generates: SELECT ... FROM (raw SQL) AS p WHERE ...

Выполнение хранимых процедур

Вызывайте хранимые процедуры, возвращающие строки сущностей, через FromSqlRaw. Для процедур, которые не возвращают строки (например, INSERT/UPDATE), используйте ExecuteSqlRawAsync.

// Stored proc returning Products
var products = await _db.Products
    .FromSqlRaw("EXEC GetProductsByCategory {0}", "Books")
    .ToListAsync();

// Stored proc with no return rows
await _db.Database.ExecuteSqlRawAsync(
    "EXEC ArchiveOldOrders {0}",
    DateTime.UtcNow.AddYears(-1));

ExecuteSqlInterpolated для DML

ExecuteSqlInterpolated выполняет инструкции DML (INSERT, UPDATE, DELETE) с безопасностью интерполированных строк. Метод возвращает количество затронутых строк.

int days = 30;
int affected = await _db.Database.ExecuteSqlInterpolatedAsync(
    $"UPDATE Orders SET Status = 'Expired' WHERE PlacedAt < {DateTime.UtcNow.AddDays(-days)}");

Console.WriteLine($"{affected} orders expired");

Запросы к типам, не являющимся сущностями

Используйте SqlQuery<T> (.NET 7+), чтобы запрашивать произвольные скалярные типы или DTO, которые не являются сущностями EF Core. DbSet не требуется.

// Query a DTO directly — no entity needed
var stats = await _db.Database
    .SqlQuery<ProductStats>(
        $"SELECT Category, COUNT(*) AS Count, AVG(Price) AS AvgPrice FROM Products GROUP BY Category")
    .ToListAsync();

public record ProductStats(string Category, int Count, decimal AvgPrice);

Типы сущностей без ключа для представлений

Сопоставьте представление базы данных или результат сырого запроса с типом сущности без ключа с помощью HasNoKey() и ToView(). Запрашивайте его как DbSet, не задавая первичный ключ.

public class ProductSalesSummary
{
    public string Category { get; set; } = "";
    public int TotalSold { get; set; }
    public decimal Revenue { get; set; }
}

// In OnModelCreating:
modelBuilder.Entity<ProductSalesSummary>()
    .HasNoKey()
    .ToView("vw_ProductSalesSummary");

// Query:
var summary = await _db.Set<ProductSalesSummary>().ToListAsync();

Как избежать ошибок, связанных с SQL-инъекциями

Золотое правило: никогда не объединяйте пользовательский ввод со строками SQL. Всегда используйте параметризованные запросы через заполнители FromSqlRaw, FromSqlInterpolated или LINQ в EF Core.

// SAFE
var name = userInput;
var result = await _db.Products
    .FromSqlInterpolated($"SELECT * FROM Products WHERE Name = {name}")
    .ToListAsync();

// INJECTION RISK (never do this)
// FromSqlRaw("SELECT * FROM Products WHERE Name = '" + name + "'");

Выходные параметры и резервный вариант ADO.NET

EF Core не поддерживает выходные параметры хранимых процедур напрямую. В таких случаях используйте ADO.NET через _db.Database.GetDbConnection().

await _db.Database.OpenConnectionAsync();
var conn = _db.Database.GetDbConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "EXEC GetOrderCount @CustomerId, @Count OUTPUT";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@CustomerId", 5));
var outParam = new SqlParameter("@Count", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(outParam);
await cmd.ExecuteNonQueryAsync();
int count = (int)outParam.Value;

Реальный пример: резервный вариант для полнотекстового поиска

С помощью LINQ нельзя выразить предикаты полнотекстового поиска, специфичные для базы данных. Используйте FromSqlInterpolated, чтобы задействовать встроенные возможности FREETEXT или tsvector.

string query = "keyboard mechanical";
var results = await _db.Products
    .FromSqlInterpolated(
        $"SELECT * FROM Products WHERE FREETEXT(Name, {query})")
    .AsNoTracking()
    .Take(20)
    .ToListAsync();

Быстрая проверка

Почему FromSqlInterpolated безопаснее объединения строк при создании параметризованных запросов?

Итоги: сырой SQL в EF Core

Главное:

  • FromSqlRaw: используйте заполнители {0}; никогда не объединяйте пользовательский ввод со строками
  • FromSqlInterpolated: безопасные интерполированные строки → параметры ADO.NET
  • Объединяйте операторы LINQ в цепочку после FromSql, чтобы добавлять фильтрацию, сортировку и разбиение на страницы
  • ExecuteSqlInterpolatedAsync: безопасное выполнение DML
  • SqlQuery<T>: запросы к DTO, не являющимся сущностями (.NET 7+)
  • Используйте ADO.NET напрямую для выходных параметров или сложных хранимых процедур

Часто задаваемые вопросы

Урок «Сырой SQL, хранимые процедуры и интерполяция» бесплатный?

Да — полный текст урока «Сырой SQL, хранимые процедуры и интерполяция» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Сырой SQL, хранимые процедуры и интерполяция»?

Безопасно выполняйте сырой SQL с помощью FromSqlRaw, FromSqlInterpolated и хранимых процедур, избегая внедрения кода. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Сырой SQL, хранимые процедуры и интерполяция»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Жадная, ленивая и явная загрузка
  2. Сырой SQL, хранимые процедуры и интерполяция
  3. Компилируемые запросы и производительность
  4. Глобальные фильтры запросов и принадлежащие сущности
← Назад к C# Academy