59

I would like to use the new Parallel.ForEach function to loop through a datatable and perform actions on each row. I am trying to convert the code below:

        foreach(DataRow drow in dt.Rows)
        {
           ...
           Do Stuff
           ...
        }

To this code:

        System.Threading.Tasks.Parallel.ForEach(dt.Rows, drow =>
                {
                    ...
                    Do Stuff
                    ...
                });

When I run the new code I get the error:

The type arguments for method 'System.Threading.Tasks.Parallel.ForEach(System.Collections.Generic.IEnumerable, System.Action)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

What is the correct syntax for this?

SchwartzE
  • 2,558
  • 5
  • 30
  • 39

5 Answers5

126

DataTable.Rows returns a DataRowCollection which only implements IEnumerable, not IEnumerable<DataRow>. Use the AsEnumerable() extension method on DataTable (from DataTableExtensions) instead:

Parallel.ForEach(dt.AsEnumerable(), drow =>
{
    ...
    Do Stuff
    ...
});
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    D'oh! Beat to the punch (by mere seconds)! – JaredReisinger Aug 04 '10 at 18:31
  • would this same extension be available for other collections that implement IEnumerable? such as the TreeNodeCollection? or would I have to create this extension myself? –  Nov 12 '10 at 12:29
  • @Scott: You'd have to write it yourself - because otherwise it's not going to know which type of `IEnumerable` to return, if you see what I mean. – Jon Skeet Nov 12 '10 at 21:54
  • 2
    Make sure to reference System.Data.DataSetExtensions – mosheb Oct 13 '13 at 01:30
  • 2
    Note that a DataTable is not Thread-Safe. You may run into "internal index is corrupted" errors. https://stackoverflow.com/questions/450675/datatable-internal-index-is-corrupted – Marc Jun 22 '17 at 06:07
  • got a lot of silliness myself, even lock()ing access... thanks for the warning @Marc – Mike M Jan 04 '18 at 22:20
  • The increase in process time is incredible! I went from 1m50 to 12 seconds. – Joeri E May 02 '22 at 15:08
18

This is better than the accepted answer because this does not need to reference System.Data.DataSetExtensions:

 Parallel.ForEach(dt.Rows.Cast<DataRow>(), dr =>

To use ForEach with a non-generic collection, you can use the Cast extension method to convert the collection to a generic collection, as shown in this example.

Kevin .NET
  • 463
  • 6
  • 9
  • 2
    But keep in mind this, from [Cast docs](https://msdn.microsoft.com/en-us/library/bb341406(v=vs.110).aspx): "If an element cannot be cast to type TResult, this method will throw an exception. To obtain only those elements that can be cast to type TResult, use the OfType method instead of Cast(IEnumerable)." – ALEXintlsos Oct 11 '17 at 21:16
8

Parallel.ForEach() expects the first argument to be an IEnumerable<> type. DataTable.Rows is not, but you can turn it into one with the AsEnumerable() extension method. Try:

... Parallel.ForEach(dt.AsEnumerable(), drow => ...
JaredReisinger
  • 6,955
  • 1
  • 22
  • 21
1

This way we can use Parallel.ForEach for Data table.

DataTable dtTest = new DataTable();
            dtTest.Columns.Add("ID",typeof(int));
            dtTest.Columns.Add("Name", typeof(string));
            dtTest.Columns.Add("Salary", typeof(int));

            DataRow dr = dtTest.NewRow();
            dr["ID"] = 1;
            dr["Name"] = "Rom";
            dr["Salary"] = "2000";
            dtTest.Rows.Add(dr);

            dr = dtTest.NewRow();
            dr["ID"] = 2;
            dr["Name"] = "David";
            dr["Salary"] = "5000";
            dtTest.Rows.Add(dr);

            dr = dtTest.NewRow();
            dr["ID"] = 3;
            dr["Name"] = "Samy";
            dr["Salary"] = "1200";
            dtTest.Rows.Add(dr);

            Parallel.ForEach(dtTest.AsEnumerable(), drow =>
            {
                MessageBox.Show("ID " + drow.Field<int>("ID") + " " + drow.Field<string>("Name") + " " + drow.Field<int>("Salary"));
            });
Indi_Rain
  • 179
  • 5
  • 17
0

I had to modify Jon Skeet's answer to make it work.

Parallel.ForEach(dt.AsEnumerable<DataRowType>(), drow => {
     drow.SomeCol = "";
});
irfandar
  • 1,690
  • 1
  • 23
  • 24