Question
I am trying to run a LINQ query on a DataTable, but it seems that querying a DataTable directly is not straightforward.
For example, I tried something like this:
var results = from myRow in myDataTable
where myRow.Field<int>("RowNo") == 1
select myRow;
This does not compile as written. How can I make a LINQ query like this work with a DataTable in C#?
I was expecting DataTable to work directly with LINQ, so I want to understand the correct approach.
Short Answer
By the end of this page, you will understand why a DataTable cannot be queried directly with standard LINQ syntax, how AsEnumerable() makes LINQ work with table rows, and how to safely read column values using Field<T>(). You will also see practical examples, common mistakes, and a small project that filters data from a DataTable.
Concept
A DataTable stores rows of data in memory, but it does not directly implement the generic sequence interface that standard LINQ queries expect.
LINQ works best with collections such as:
IEnumerable<T>List<T>- arrays
- other generic sequences
A DataTable contains rows as DataRow objects, but to use LINQ query syntax like from ... where ... select ..., you usually need to convert the table into something LINQ can enumerate as a sequence of rows.
That is why C# provides the AsEnumerable() extension method for DataTable.
var results = myDataTable.AsEnumerable();
This gives you an IEnumerable<DataRow>, which LINQ can query.
Another important part is reading values from a DataRow. A DataRow stores column values as objects, so LINQ queries often use:
row.Field<int>("RowNo")
The Field<T>() method:
- reads a typed value from a column
- avoids manual casting in many cases
- works better with nullable values than direct indexing alone
This matters in real programming because many legacy systems, reporting tools, WinForms apps, and database-heavy .NET applications still use DataTable. Knowing how to bridge DataTable and LINQ makes filtering, sorting, and projecting data much cleaner.
Mental Model
Think of a DataTable as a filing cabinet and each DataRow as one paper file.
LINQ expects a line of items it can walk through one by one, like files laid out on a desk. But a DataTable is not automatically presented that way for generic LINQ.
AsEnumerable() is the step that takes the files out of the cabinet and lays them on the desk so LINQ can inspect them one at a time.
Then Field<T>() is like reading a labeled field from each paper file in the correct data type:
"RowNo"as anint"Name"as astring"Price"as adecimal
So the process is:
- Convert the
DataTableinto an enumerable row sequence. - Read values from each row by column name.
- Filter or transform the rows with LINQ.
Syntax and Examples
The basic pattern is:
using System.Data;
using System.Linq;
var results = from row in myDataTable.AsEnumerable()
where row.Field<int>("RowNo") == 1
select row;
You can also write the same query with method syntax:
var results = myDataTable.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 1);
Example: filter rows by column value
using System;
using System.Data;
using System.Linq;
DataTable table = new DataTable();
table.Columns.Add("RowNo", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Rows.Add(1, "Alice");
table.Rows.Add(2, "Bob");
table.Rows.Add(1, "Charlie");
var results = from row in table.AsEnumerable()
where row.Field<int>("RowNo") == 1
select row;
foreach (var row in results)
{
Console.WriteLine($"{row.Field<int>("RowNo")} - {row.Field<string>("Name")}");
}
Output:
1 - Alice
1 - Charlie
Example: select specific columns instead of full rows
var names = from row in table.AsEnumerable()
where row.Field<int>("RowNo") == 1
select row.Field<string>("Name");
This returns a sequence of names instead of DataRow objects.
Example: convert results back to a DataTable
If you need the filtered result as another DataTable:
var filteredRows = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 1);
DataTable filteredTable = filteredRows.CopyToDataTable();
This is useful when older APIs still expect a DataTable instead of an IEnumerable<DataRow>.
Step by Step Execution
Consider this code:
DataTable table = new DataTable();
table.Columns.Add("RowNo", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Rows.Add(1, "Alice");
table.Rows.Add(2, "Bob");
table.Rows.Add(1, "Charlie");
var results = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 1)
.Select(row => row.Field<string>("Name"));
Here is what happens step by step:
- A
DataTableis created. - Two columns are added:
RowNoasintNameasstring
- Three rows are inserted.
AsEnumerable()exposes the rows asIEnumerable<DataRow>.Where(...)checks each row:- Row 1:
RowNois1→ keep it - Row 2:
RowNois2→ skip it - Row 3:
RowNois1→ keep it
- Row 1:
Select(...)transforms the remaining rows into just theNamevalues.- The final result is a sequence containing:
Alice
Charlie
A key detail is that LINQ often uses deferred execution. The query is not fully executed when you define results. It runs when you iterate over it, for example with foreach or by calling .ToList().
Real World Use Cases
LINQ on DataTable is useful in many existing .NET applications.
Filtering imported data
If you load CSV or Excel data into a DataTable, you can use LINQ to find rows matching conditions:
- invalid records
- duplicate IDs
- rows missing required values
Working with database results
Some older systems still use SqlDataAdapter to fill a DataTable. LINQ makes it easier to:
- filter active users
- sort invoice rows
- extract only needed columns
Reporting and dashboards
A report may gather raw data into a DataTable, then use LINQ to:
- group values
- compute totals
- display only a subset of rows
Validation pipelines
Before saving data, LINQ can help detect:
- missing fields
- out-of-range values
- duplicate business keys
Real Codebase Usage
In real projects, developers often use LINQ on DataTable in small, focused ways rather than building huge query expressions.
Common patterns
Guard clauses before querying
if (table == null || table.Rows.Count == 0)
{
return;
}
This prevents null-reference problems and pointless queries.
Validation before reading typed values
var validRows = table.AsEnumerable()
.Where(row => !row.IsNull("RowNo"));
This is helpful when database data may contain DBNull.
Early filtering
var activeRows = table.AsEnumerable()
.Where(row => row.Field<bool>("IsActive"));
Filter early so later logic works on a smaller set.
Projection into models
Instead of passing DataRow around, many codebases map rows into objects:
var users = table.AsEnumerable()
.Select(row => new
{
Id = row.Field<int>("Id"),
Name = row.Field<string>("Name")
});
This makes business logic cleaner and easier to test.
Convert back only when necessary
If some older API requires a DataTable, developers may filter with LINQ and then call CopyToDataTable(). But if possible, they keep the result as a sequence or map it into domain objects.
Common Mistakes
1. Querying the DataTable directly
Broken code:
var results = from row in myDataTable
where row.Field<int>("RowNo") == 1
select row;
Why it fails:
DataTableis not directly used asIEnumerable<DataRow>for this query syntax.
Fix:
var results = from row in myDataTable.AsEnumerable()
where row.Field<int>("RowNo") == 1
select row;
2. Using the wrong variable name inside the query
Broken code:
var results = from myRow in myDataTable.AsEnumerable()
where results.Field<int>("RowNo") == 1
select results;
Why it fails:
resultsis the query result variable, not the current row.- Inside the query, use
myRow.
Fix:
var results = from myRow in myDataTable.AsEnumerable()
where myRow.Field<int>("RowNo") == 1
select myRow;
3. Forgetting the type in Field<T>()
Broken code:
where row.Field("RowNo") == 1
Why it fails:
Fieldis generic and needs the type.
Fix:
where row.Field<int>("RowNo") == 1
4. Using the wrong type
Broken code:
where row.Field<string>("RowNo") == "1"
If the column is actually an integer, use int.
where row.Field<int>("RowNo") == 1
5. Ignoring DBNull
Broken code:
var ids = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") > 0);
If RowNo can be null in the table, this may fail.
Safer version:
var ids = table.AsEnumerable()
.Where(row => !row.IsNull("RowNo"))
.Where(row => row.Field<int>("RowNo") > 0);
Or with nullable types:
var ids = table.AsEnumerable()
.Where(row => row.Field<int?>("RowNo") == 1);
6. Calling CopyToDataTable() on an empty result
Broken code:
DataTable filtered = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 99)
.CopyToDataTable();
Why it fails:
CopyToDataTable()throws if there are no rows.
Safer approach:
var filteredRows = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 99);
DataTable filtered = filteredRows.Any()
? filteredRows.CopyToDataTable()
: table.Clone();
Comparisons
| Concept | What it works on | Best for | Notes |
|---|---|---|---|
DataTable.Select() | DataTable | Simple filtering with string expressions | Older API, less type-safe |
LINQ with AsEnumerable() | DataTable rows as IEnumerable<DataRow> | Readable filtering, projection, composition | Preferred for modern C# style |
List<T> with LINQ | Strongly typed objects | Most application business logic | Cleaner than raw DataTable usage |
DataTable.Select() vs LINQ
DataTable.Select()
DataRow[] rows = table.Select("RowNo = 1");
Pros:
- short for simple expressions
- built into
DataTable
Cons:
- string-based conditions
- less compile-time checking
- harder to refactor
LINQ
var rows = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 1);
Pros:
- type-safe access with
Field<T>() - easier to combine with
Select,OrderBy,GroupBy - more readable in larger codebases
Cons:
- needs
AsEnumerable() - may feel more verbose at first
DataRow vs custom object
| Option | Advantages | Disadvantages |
|---|---|---|
DataRow | Easy when data is already in a DataTable | Column names are strings, less expressive |
| Custom class | Strong typing, cleaner code, easier testing | Requires mapping step |
In many real applications, developers query the DataTable first, then project the rows into typed objects.
Cheat Sheet
// Required namespaces
using System.Data;
using System.Linq;
// Basic LINQ query on a DataTable
var results = from row in table.AsEnumerable()
where row.Field<int>("RowNo") == 1
select row;
// Method syntax
var results2 = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 1);
// Select one column
var names = table.AsEnumerable()
.Select(row => row.Field<string>("Name"));
// Handle nullable values
var rows = table.AsEnumerable()
.Where(row => row.Field<int?>("RowNo") == 1);
// Check for DBNull
var safeRows = table.AsEnumerable()
.Where(row => !row.IsNull("RowNo"));
// Convert filtered rows back to DataTable
var filteredRows = table.AsEnumerable()
.Where(row => row.Field<int>("RowNo") == 1);
DataTable filteredTable = filteredRows.Any()
? filteredRows.CopyToDataTable()
: table.Clone();
Rules to remember
- Use
AsEnumerable()before querying aDataTablewith LINQ. - Use
Field<T>("ColumnName")to read typed column values. - Use the correct generic type in
Field<T>(). - Watch for
DBNullvalues. CopyToDataTable()fails on empty sequences.- Prefer projecting into typed objects when possible.
FAQ
Why can't I use LINQ directly on a DataTable?
DataTable is not directly queried the same way as a generic IEnumerable<T> source in normal LINQ syntax. Use AsEnumerable() to expose its rows as IEnumerable<DataRow>.
What namespace do I need for AsEnumerable()?
You typically need System.Data and System.Linq. In many projects, the extension method support for DataTable also comes from the DataSet extensions assembly available in .NET.
What does Field() do in a DataRow?
It reads a column value from a DataRow and returns it as the specified type, such as int, string, or DateTime.
Can I return a DataTable after filtering with LINQ?
Yes. After filtering rows, call CopyToDataTable(). If the result may be empty, check with Any() first or return table.Clone().
Is DataTable.Select() better than LINQ?
Not usually. DataTable.Select() is fine for simple string-based filters, but LINQ is generally more readable, composable, and type-safe.
How do I handle null values in DataTable LINQ queries?
Use row.IsNull("ColumnName") or read with a nullable type like Field<int?>().
Should I keep using DataTable in new code?
If possible, strongly typed models are usually easier to maintain. But when you already have a DataTable, LINQ is a clean way to work with it.
Mini Project
Description
Build a small in-memory employee filter using a DataTable and LINQ. This project demonstrates how to create a DataTable, add rows, filter by a numeric column, and project the results into readable output. It is useful because many .NET applications still receive tabular data from databases, files, or older APIs.
Goal
Create a program that stores employee data in a DataTable and prints only employees from a chosen department.
Requirements
- Create a
DataTablewith columns forId,Name, andDepartmentId. - Add at least five sample employee rows.
- Use
AsEnumerable()and LINQ to filter employees whereDepartmentIdequals 1. - Print the matching employee names to the console.
- Also show a version that projects matching rows into anonymous objects.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.