Avatar ·

LINQ запрос к DataTable в C#: как выполнить выборку данных?

Всем привет! Я начинающий C# разработчик, работаю с .NET Framework 3.5. У меня есть DataTable, заполненный из базы данных. Мне нужно выполнить выборку строк по определенному условию, например, найти всех сотрудников с возрастом больше 30 лет. Проблема в том, что DataTable не поддерживает LINQ напрямую. Когда я пытаюсь написать что-то вроде: ```csharp DataTable dt = GetEmployeesTable(); var result = from row in dt where row["Age"] > 30 select row; ``` Компилятор выдает ошибку: "Could not find an implementation of the query pattern for source type 'System.Data.DataTable'." Я понимаю, что DataTable — это не коллекция объектов, а таблица со строками и столбцами. Но как тогда применить LINQ? Я слышал про метод AsEnumerable(), но не уверен, как его правильно использовать. Также меня интересует, можно ли после выборки преобразовать результат обратно в DataTable? И как обращаться к столбцам — по имени или по индексу? Буду благодарен за подробный пример с объяснением. Спасибо! <<>>

Решение: используйте метод AsEnumerable() и Field<T>()

Действительно, DataTable не реализует интерфейс IEnumerable<T>, необходимый для LINQ. Однако в .NET 3.5 и выше есть метод расширения AsEnumerable(), который возвращает EnumerableRowCollection<DataRow>. Это позволяет применять LINQ к строкам DataTable.

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

using System.Data; // обязательно добавьте эту директиву

DataTable dt = GetEmployeesTable();
var result = from row in dt.AsEnumerable()
             where row.Field<int>("Age") > 30
             select row;

Обратите внимание на метод Field<T>("ColumnName"). Он безопасно извлекает значение столбца с приведением к нужному типу. Это лучше, чем прямое обращение через row["Age"], так как:

  • Избегает исключений при null значениях (возвращает default(T) для типов-значений)
  • Работает быстрее, чем boxing/unboxing
  • Обеспечивает строгую типизацию

Как преобразовать результат обратно в DataTable?

Если вам нужно получить новый DataTable из результата запроса, используйте метод CopyToDataTable():

DataTable filteredDt = dt.AsEnumerable()
    .Where(row => row.Field<int>("Age") > 30)
    .CopyToDataTable();

Важно: CopyToDataTable() доступен в пространстве имен System.Data.DataSetExtensions. Убедитесь, что у вас есть ссылка на сборку System.Data.DataSetExtensions.dll.

Полный пример с пояснениями

using System;
using System.Data;
using System.Linq;

class Program
{
    static void Main()
    {
        // Создаем DataTable
        DataTable dt = new DataTable("Employees");
        dt.Columns.Add("Id", typeof(int));
        dt.Columns.Add("Name", typeof(string));
        dt.Columns.Add("Age", typeof(int));

        // Заполняем данными
        dt.Rows.Add(1, "Иван", 25);
        dt.Rows.Add(2, "Мария", 35);
        dt.Rows.Add(3, "Петр", 40);

        // LINQ запрос: сотрудники старше 30
        var adults = from row in dt.AsEnumerable()
                     where row.Field<int>("Age") > 30
                     select new
                     {
                         Id = row.Field<int>("Id"),
                         Name = row.Field<string>("Name"),
                         Age = row.Field<int>("Age")
                     };

        // Вывод результатов
        foreach (var emp in adults)
        {
            Console.WriteLine($"{emp.Id}: {emp.Name}, {emp.Age} лет");
        }

        // Или сразу в новый DataTable
        DataTable filteredDt = dt.AsEnumerable()
            .Where(r => r.Field<int>("Age") > 30)
            .CopyToDataTable();
    }
}

Важные замечания

  • Всегда используйте Field<T> для типизированного доступа к столбцам
  • Если столбец может быть null, используйте Field<int?>("Age") (nullable тип)
  • Метод AsEnumerable() не копирует данные — он просто предоставляет итератор по строкам
  • Для обратной совместимости с .NET 2.0 можно использовать Select и Where из System.Data, но LINQ удобнее
<<>>

Альтернативный подход: использование Select() и Where() из DataTable

Если вы не можете использовать LINQ (например, работаете с .NET Framework 2.0), можно воспользоваться встроенными методами DataTable — Select() и Compute().

DataTable dt = GetEmployeesTable();
// Фильтрация через строку условия (синтаксис, похожий на SQL)
DataRow[] filteredRows = dt.Select("Age > 30");

// Можно сортировать
DataRow[] sortedRows = dt.Select("Age > 30", "Name ASC");

// Создаем новый DataTable из результата
DataTable result = dt.Clone(); // копируем структуру
foreach (DataRow row in filteredRows)
{
    result.ImportRow(row);
}

Этот подход:

  • Не требует дополнительных сборок (работает "из коробки")
  • Проще для простых фильтраций
  • Однако менее гибкий, чем LINQ: нет строгой типизации, сложнее делать сложные предикаты

Когда использовать: для быстрых прототипов или если вы ограничены старой версией .NET. Для современных проектов предпочтительнее LINQ с AsEnumerable().