1140

I'm trying to perform a LINQ query on a DataTable object and bizarrely I am finding that performing such queries on DataTables is not straightforward. For example:

var results = from myRow in myDataTable
where results.Field("RowNo") == 1
select results;

This is not allowed. How do I get something like this working?

I'm amazed that LINQ queries are not allowed on DataTables!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Calanus
  • 25,619
  • 25
  • 85
  • 120
  • 3
    You can find more LINQ/Lambda example from http://webmingle.blogspot.com/2010_09_01_archive.html –  Feb 17 '11 at 19:18
  • It's because datatables predate LINQ by some number of years. Use a strongly typed datatable instead; a better experience all round than this stringly-typed, intellisense-defeating `dt.Rows["FirstName]` junk.. With a strongly typed table (add a DataSet type file to your project and create tables inside it in the visual designer) you just write e.g. `myStronglyTpedDataset.Person.Where(p => p.FirstName == "John")` - all the magic to make it happen is already done – Caius Jard Nov 25 '20 at 11:11
  • @CaiusJard does that mean that you would have to create DataSet type file for every query? – symbiont Apr 29 '21 at 13:46
  • Not for every query, no. The dataset represents the entities being modelled, so in the same way that you might have an Order entity that has a list of Product entity in an EF application, your strongly typed dataset has a OrdersDataTable that has a relation to a ProductsDataTable. It's similar effort to create them too if you have a DB, as EF can scaffold entities from an existing db, as can the dataset designer. If you were adding new entities to your code it's slightly easier with datasets; you just click click add a table, click add columns etc and in the background VS is writing your code – Caius Jard Apr 29 '21 at 14:48
  • So all that's necessary here is one `AsEnumerable()` call. No idea why that requires so many answers. – Gert Arnold Sep 23 '21 at 13:24

22 Answers22

1406

You can't query against the DataTable's Rows collection, since DataRowCollection doesn't implement IEnumerable<T>. You need to use the AsEnumerable() extension for DataTable. Like so:

var results = from myRow in myDataTable.AsEnumerable()
where myRow.Field<int>("RowNo") == 1
select myRow;

And as @Keith says, you'll need to add a reference to System.Data.DataSetExtensions

AsEnumerable() returns IEnumerable<DataRow>. If you need to convert IEnumerable<DataRow> to a DataTable, use the CopyToDataTable() extension.

Below is query with Lambda Expression,

var result = myDataTable
    .AsEnumerable()
    .Where(myRow => myRow.Field<int>("RowNo") == 1);
Collin K
  • 15,277
  • 1
  • 27
  • 22
  • 8
    VB Version: Dim results = From myRow In myDataTable.AsEnumerable _ Where myRow.Field("RowNo") = 1 _ Select myRow – Jeff Jul 29 '09 at 20:46
  • 17
    I already had a reference to the dll mentioned, but was missing `using System.Data;` – Luke Duddridge May 31 '11 at 10:37
  • 3
    How do I get DataTable back from var results ? – Cannon Aug 11 '11 at 17:50
  • 5
    VB Version needs to insert (Of String) between myRow.Field and ("RowNo"). That part should read: myRow.Field(Of String)("RowNo") = 1 - Reference @Cros comment. – yougotiger Jun 18 '12 at 22:26
  • 8
    this solution is needlessly complicated. Use `myDataTable.Rows` instead as @JoelFan suggested. – The Conspiracy Jun 25 '14 at 18:46
  • 11
    @Markus Just to clarify, the reason that @JoelFan's solution works with `myDataTable.Rows` is because the `myRow` variable is explicitly cast to `DataRow`. When it is compiled, that query is rewritten to `myDataTable.Rows.Cast().Where(myRow => (int)myRow["RowNo"] == 1)`. Personally, I don't find the call to `AsEnumerable()` any more complicated than the call to `Cast()`. As far as I know, the performance is the same, so it's just a matter of preference. – Collin K Jun 26 '14 at 16:29
  • 3
    `...If you need to convert IEnumerable to aDataTable, use the CopyToDataTable() extension.` this I have ben searching for ;) – bonCodigo Jul 12 '14 at 00:15
  • is it faster than using for loop ? – dellos Jan 21 '22 at 09:18
141
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow
JoelFan
  • 37,465
  • 35
  • 132
  • 205
  • 2
    What about for selecting multiple rows, instead of just row 1? – Adjit Apr 29 '16 at 15:35
  • 2
    Just remove the "where" line and you will get all the rows – JoelFan May 01 '16 at 02:48
  • 2
    Yes, this is how I use to do it, except for replacing `(int)myRow["RowNo"]` with the generic form `myRow.Field("RowNo")` to more conveniently support nullable types. – Jonas Aug 25 '17 at 14:35
75

It's not that they were deliberately not allowed on DataTables, it's just that DataTables pre-date the IQueryable and generic IEnumerable constructs on which Linq queries can be performed.

Both interfaces require some sort type-safety validation. DataTables are not strongly typed. This is the same reason why people can't query against an ArrayList, for example.

For Linq to work you need to map your results against type-safe objects and query against that instead.

Jon Limjap
  • 94,284
  • 15
  • 101
  • 152
54

As @ch00k said:

using System.Data; //needed for the extension methods to work

...

var results = 
    from myRow in myDataTable.Rows 
    where myRow.Field<int>("RowNo") == 1 
    select myRow; //select the thing you want, not the collection

You also need to add a project reference to System.Data.DataSetExtensions

Keith
  • 150,284
  • 78
  • 298
  • 434
  • 2
    If you try this, you'll find it won't work unless you put a specific type on `myRow` or use `Cast()` on `Rows`. Better to use `AsEnumerable()`. – NetMage Jan 15 '20 at 23:48
  • 1
    @NetMage this worked 12 years ago when I posted it. As long as you have `System.Linq` and `System.Data.DataSetExtensions` then `myDataTable.Rows` returns an enumerable collection of `DataRow` anyway. That might have changed, it's been a decade since I've used it. – Keith Jan 16 '20 at 07:24
  • 1
    Interesting - I guess it was changed at some point, as it doesn't work on .Net or .Net Core now. – NetMage Jan 16 '20 at 19:06
  • 1
    @NetMage yes, I'm not surprised the `DataSet` extensions didn't make it into .NET Core or .NET Standard, they were already outdated when I posted this answer. I really wouldn't use `DataSet` in new projects, there are far better data access models, both for ease of coding and performance. – Keith Jan 16 '20 at 23:16
  • 2
    They are there, but `DataRowCollection` doesn't implement `IEnumerable` just `IEnumerable` and so doesn't work with strongly typed LINQ. – NetMage Jan 16 '20 at 23:19
47

I realize this has been answered a few times over, but just to offer another approach:

I like to use the .Cast<T>() method, it helps me maintain sanity in seeing the explicit type defined and deep down I think .AsEnumerable() calls it anyways:

var results = from myRow in myDataTable.Rows.Cast<DataRow>() 
                  where myRow.Field<int>("RowNo") == 1 select myRow;

or

var results = myDataTable.Rows.Cast<DataRow>()
                  .FirstOrDefault(x => x.Field<int>("RowNo") == 1);

As noted in comments, does not require System.Data.DataSetExtensions or any other assemblies (Reference)

vandsh
  • 1,329
  • 15
  • 12
40
var query = from p in dt.AsEnumerable()
                    where p.Field<string>("code") == this.txtCat.Text
                    select new
                    {
                        name = p.Field<string>("name"),
                        age= p.Field<int>("age")                         
                    };

the name and age fields are now part of the query object and can be accessed like so: Console.WriteLine(query.name);

LC0815
  • 13
  • 4
Ravi
  • 439
  • 4
  • 2
  • 1
    How I use name? For example, `MessageBox.Show(name)` is undefined. –  Sep 30 '14 at 22:54
34

Using LINQ to manipulate data in DataSet/DataTable

var results = from myRow in tblCurrentStock.AsEnumerable()
              where myRow.Field<string>("item_name").ToUpper().StartsWith(tbSearchItem.Text.ToUpper())
              select myRow;
DataView view = results.AsDataView();
Max
  • 21,123
  • 5
  • 49
  • 71
Salim
  • 349
  • 3
  • 2
  • 1
    The AsDataView doesn't appear in Intellisense for me. I included using System.Data.Linq and using System.Linq but still it's not working. Do you know what am I missing? Thanks in advance. – Naomi May 09 '13 at 19:27
  • @Naomi It comes from `System.Data.DataSetExtensions`. – Louis Waweru Feb 19 '14 at 16:51
30
//Create DataTable 
DataTable dt= new DataTable();
dt.Columns.AddRange(new DataColumn[]
{
   new DataColumn("ID",typeof(System.Int32)),
   new DataColumn("Name",typeof(System.String))

});

//Fill with data

dt.Rows.Add(new Object[]{1,"Test1"});
dt.Rows.Add(new Object[]{2,"Test2"});

//Now  Query DataTable with linq
//To work with linq it should required our source implement IEnumerable interface.
//But DataTable not Implement IEnumerable interface
//So we call DataTable Extension method  i.e AsEnumerable() this will return EnumerableRowCollection<DataRow>


// Now Query DataTable to find Row whoes ID=1

DataRow drow = dt.AsEnumerable().Where(p=>p.Field<Int32>(0)==1).FirstOrDefault();
 // 
Muhammad Musavi
  • 2,512
  • 2
  • 22
  • 35
sushil pandey
  • 752
  • 10
  • 9
25

Try this simple line of query:

var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);
Paul Zahra
  • 9,522
  • 8
  • 54
  • 76
Mohit Verma
  • 5,140
  • 2
  • 12
  • 27
17

You can use LINQ to objects on the Rows collection, like so:

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
David Wengier
  • 10,061
  • 5
  • 39
  • 43
  • 2
    Because `DataTable.Rows` does not implement `IEnumerable`, I can't see how this query could compile. – onedaywhen Jul 11 '16 at 08:45
  • @onedaywhen I just saw this being done in some code and it does compile. Trying to figure out why right now. – BVernon Jan 26 '18 at 22:42
  • ... or you can just use a filter expression within the Select method : var results = myDataTable.Select("RowNo=1"); This returns a DataRow array. – Ishikawa Dec 16 '19 at 11:48
14

This is a simple way that works for me and uses lambda expressions:

var results = myDataTable.Select("").FirstOrDefault(x => (int)x["RowNo"] == 1)

Then if you want a particular value:

if(results != null) 
    var foo = results["ColName"].ToString()
Matt Kemp
  • 2,742
  • 2
  • 28
  • 38
11

Try this

var row = (from result in dt.AsEnumerable().OrderBy( result => Guid.NewGuid()) select result).Take(3) ; 
Marijn
  • 10,367
  • 5
  • 59
  • 80
midhun sankar
  • 119
  • 1
  • 2
11

Most likely, the classes for the DataSet, DataTable and DataRow are already defined in the solution. If that's the case you won't need the DataSetExtensions reference.

Ex. DataSet class name-> CustomSet, DataRow class name-> CustomTableRow (with defined columns: RowNo, ...)

var result = from myRow in myDataTable.Rows.OfType<CustomSet.CustomTableRow>()
             where myRow.RowNo == 1
             select myRow;

Or (as I prefer)

var result = myDataTable.Rows.OfType<CustomSet.CustomTableRow>().Where(myRow => myRow.RowNo);
Paul Zahra
  • 9,522
  • 8
  • 54
  • 76
xadriel
  • 191
  • 2
  • 6
9
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;
falsetru
  • 357,413
  • 63
  • 732
  • 636
Vinay
  • 179
  • 2
  • 2
8

In my application I found that using LINQ to Datasets with the AsEnumerable() extension for DataTable as suggested in the answer was extremely slow. If you're interested in optimizing for speed, use James Newtonking's Json.Net library (http://james.newtonking.com/json/help/index.html)

// Serialize the DataTable to a json string
string serializedTable = JsonConvert.SerializeObject(myDataTable);    
Jarray dataRows = Jarray.Parse(serializedTable);

// Run the LINQ query
List<JToken> results = (from row in dataRows
                    where (int) row["ans_key"] == 42
                    select row).ToList();

// If you need the results to be in a DataTable
string jsonResults = JsonConvert.SerializeObject(results);
DataTable resultsTable = JsonConvert.DeserializeObject<DataTable>(jsonResults);
LandedGently
  • 693
  • 1
  • 8
  • 16
  • I doubt this is faster, in the general cases. It has the overhead of two serialization, one deserialization and one parsing operations. Regardless, I downvoted because it is not concise, i.e. the serialization/deserialization doesn't make clear that the intent is to filter a list. – an phu Aug 07 '15 at 22:09
  • @an phu, using the .AsEnumerable extension method creates a collection of heavyweight `System.Data.DataRow` objects. The serialized and parsed data table creates lightweight data consisting only of the column names and values of each row. When the query runs, it will load the data into memory, which for a large dataset may involve swapping. Sometimes, the overhead of several operations is less than the overhead of copying large amounts of data in and out of memory. – LandedGently Aug 10 '15 at 19:42
8

Example on how to achieve this provided below:

DataSet dataSet = new DataSet(); //Create a dataset
dataSet = _DataEntryDataLayer.ReadResults(); //Call to the dataLayer to return the data

//LINQ query on a DataTable
var dataList = dataSet.Tables["DataTable"]
              .AsEnumerable()
              .Select(i => new
              {
                 ID = i["ID"],
                 Name = i["Name"]
               }).ToList();
Ryan Gavin
  • 689
  • 1
  • 8
  • 22
7

For VB.NET The code will look like this:

Dim results = From myRow In myDataTable  
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow
Abdul Saboor
  • 4,079
  • 2
  • 33
  • 25
7
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
                             select myRow["server"].ToString() ;
Iman
  • 17,932
  • 6
  • 80
  • 90
6

Try this...

SqlCommand cmd = new SqlCommand( "Select * from Employee",con);
SqlDataReader dr = cmd.ExecuteReader( );
DataTable dt = new DataTable( "Employee" );
dt.Load( dr );
var Data = dt.AsEnumerable( );
var names = from emp in Data select emp.Field<String>( dt.Columns[1] );
foreach( var name in names )
{
    Console.WriteLine( name );
}
Tshilidzi Mudau
  • 7,373
  • 6
  • 36
  • 49
Uthaiah
  • 1,283
  • 13
  • 14
5

You can get it work elegant via linq like this:

from prod in TenMostExpensiveProducts().Tables[0].AsEnumerable()
where prod.Field<decimal>("UnitPrice") > 62.500M
select prod

Or like dynamic linq this (AsDynamic is called directly on DataSet):

TenMostExpensiveProducts().AsDynamic().Where (x => x.UnitPrice > 62.500M)

I prefer the last approach while is is the most flexible. P.S.: Don't forget to connect System.Data.DataSetExtensions.dll reference

AuthorProxy
  • 7,946
  • 3
  • 27
  • 40
5

you can try this, but you must be sure the type of values for each Column

List<MyClass> result = myDataTable.AsEnumerable().Select(x=> new MyClass(){
     Property1 = (string)x.Field<string>("ColumnName1"),
     Property2 = (int)x.Field<int>("ColumnName2"),
     Property3 = (bool)x.Field<bool>("ColumnName3"),    
});
0

I propose following solution:

DataView view = new DataView(myDataTable); 
view.RowFilter = "RowNo = 1";
DataTable results = view.ToTable(true);

Looking at the DataView Documentation, the first thing we can see is this:

Represents a databindable, customized view of a DataTable for sorting, filtering, searching, editing, and navigation.

What I am getting from this is that DataTable is meant to only store data and DataView is there enable us to "query" against the DataTable.

Here is how this works in this particular case:

You try to implement the SQL Statement

SELECT *
FROM myDataTable
WHERE RowNo = 1

in "DataTable language". In C# we would read it like this:

FROM myDataTable
WHERE RowNo = 1
SELECT *

which looks in C# like this:

DataView view = new DataView(myDataTable);  //FROM myDataTable
view.RowFilter = "RowNo = 1";  //WHERE RowNo = 1
DataTable results = view.ToTable(true);  //SELECT *
Alan
  • 949
  • 8
  • 26
  • 1
    This isn't really true at all I'm afraid. DataView are useful but they aren't intended to implement the sophisticated sorts of data operations LINQ does – Caius Jard Apr 29 '21 at 14:54