LINQ запросы к DataTable в C#: ультимативный гайд с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с DataTable через LINQ: от новичка до мастера за 20 минут

Представьте ситуацию: вы пишете приложение на C#, загружаете данные из базы в DataTable, а затем… вам нужно отфильтровать строки по сложному условию, сгруппировать по дате или объединить две таблицы. Старый добрый DataTable.Select("...") с его строковыми фильтрами ломается на втором условии, а циклы foreach превращают код в спагетти.

Знакомо? Тогда добро пожаловать в мир LINQ запросов к DataTable. Это — ваш супер-инструмент, который превращает работу с табличными данными в элегантный, читаемый и мощный код. В этом руководстве мы разберём всё: от базовых Where до продвинутых Join и GroupBy. Поехали!


Что такое LINQ to DataSet и зачем он нужен?

LINQ (Language Integrated Query) — это встроенный в C# язык запросов. Его главная фишка: вы пишете запросы к любым данным (массивы, списки, XML, базы данных) одинаково — с помощью знакомых операторов from, where, select.

Но с DataTable есть нюанс: напрямую LINQ к нему не применить. DataTable не реализует интерфейс IEnumerable<T>. Решение — метод AsEnumerable(), который превращает таблицу в коллекцию объектов DataRow. Именно с ней и работает LINQ.

Плюсы подхода:

  • ✅ Типобезопасность: компилятор ловит ошибки в названиях колонок
  • ✅ Читаемость: код похож на SQL, но остаётся C#
  • ✅ Мощь: фильтрация, проекция, группировка, сортировка — одной строкой
  • ✅ Производительность: отложенное выполнение (не загружает лишнего)

Давайте сразу к делу. Установите в проекте ссылку на System.Data.DataSetExtensions (обычно уже есть) — и вперёд.


Подготовка данных: наша тестовая таблица

Чтобы не быть голословными, создадим DataTable с данными о товарах. Код ниже — наша база для всех примеров:

// 1. Создаём таблицу с колонками
DataTable products = new DataTable("Products");
products.Columns.Add("Id", typeof(int));
products.Columns.Add("Name", typeof(string));
products.Columns.Add("Category", typeof(string));
products.Columns.Add("Price", typeof(decimal));
products.Columns.Add("DateAdded", typeof(DateTime));

// 2. Добавляем строки
products.Rows.Add(1, "Ноутбук ASUS", "Электроника", 55000, DateTime.Now.AddDays(-10));
products.Rows.Add(2, "Мышь Logitech", "Электроника", 2500, DateTime.Now.AddDays(-5));
products.Rows.Add(3, "Книга 'C# in Depth'", "Книги", 1800, DateTime.Now.AddDays(-2));
products.Rows.Add(4, "Наушники Sony", "Электроника", 12000, DateTime.Now.AddDays(-1));
products.Rows.Add(5, "Блокнот Moleskine", "Канцелярия", 1200, DateTime.Now.AddDays(-20));
products.Rows.Add(6, "Ручка Parker", "Канцелярия", 3500, DateTime.Now.AddDays(-15));
products.Rows.Add(7, "Монитор Dell", "Электроника", 32000, DateTime.Now.AddDays(-30));

Теперь у нас есть 7 товаров. Пора применить магию LINQ.


Базовые LINQ запросы к DataTable

1. Фильтрация (Where) — как SQL WHERE

Допустим, нужно выбрать все товары дороже 10 000 рублей. Без LINQ вы бы писали цикл с проверкой row["Price"]. С LINQ это выглядит так:

var expensiveProducts = products.AsEnumerable()
    .Where(row => row.Field<decimal>("Price") > 10000);

foreach (var row in expensiveProducts)
{
    Console.WriteLine($"{row["Name"]} - {row["Price"]} руб.");
}
// Вывод: Ноутбук ASUS - 55000, Наушники Sony - 12000, Монитор Dell - 32000

Важный момент: используйте метод Field<T>("ColumnName") вместо прямого приведения (decimal)row["Price"]. Это безопаснее: если колонка DBNull, Field вернёт null (для ссылочных типов) или выбросит исключение (для значимых). Всегда используйте Field!

2. Проекция (Select) — выбираем нужные колонки

Хотите получить только названия и цены, да ещё и с налогом? Легко:

var productInfos = products.AsEnumerable()
    .Select(row => new
    {
        Name = row.Field<string>("Name"),
        PriceWithTax = row.Field<decimal>("Price") * 1.2m
    });

foreach (var item in productInfos)
    Console.WriteLine($"{item.Name}: {item.PriceWithTax:F2} руб.");

Обратите внимание: мы создаём анонимный тип на лету. Это удобно, когда не нужно возвращать целую строку DataRow.

3. Сортировка (OrderBy, ThenBy)

Отсортируем товары по цене по возрастанию, а затем по дате добавления:

var sortedProducts = products.AsEnumerable()
    .OrderBy(row => row.Field<decimal>("Price"))
    .ThenByDescending(row => row.Field<DateTime>("DateAdded"));

// Для убывания: OrderByDescending()

Продвинутые техники: группировка и объединение

4. Группировка (GroupBy) — считаем статистику по категориям

Как быстро узнать среднюю цену товара по каждой категории? SQL-запрос GROUP BY в C#:

var categoryStats = products.AsEnumerable()
    .GroupBy(row => row.Field<string>("Category"))
    .Select(group => new
    {
        Category = group.Key,
        Count = group.Count(),
        AvgPrice = group.Average(r => r.Field<decimal>("Price")),
        TotalPrice = group.Sum(r => r.Field<decimal>("Price"))
    });

foreach (var stat in categoryStats)
{
    Console.WriteLine($"{stat.Category}: {stat.Count} товаров, " +
                      $"средняя цена {stat.AvgPrice:F2} руб.");
}
// Вывод:
// Электроника: 4 товаров, средняя цена 25375,00 руб.
// Книги: 1 товаров, средняя цена 1800,00 руб.
// Канцелярия: 2 товаров, средняя цена 2350,00 руб.

Совет: GroupBy — один из самых полезных операторов. С его помощью можно делать любые агрегации: Min, Max, Average,

Блоги

Рекомендаций по книгам