TL;DR
You can do this with the following code, but please read on for more details:
<!-- language: lang-vb -->
change = Planned_Table.AsEnumerable _
.Except(Actual_Table.AsEnumerable, DataRowComparer.Default) _
.CopyToDataTable
There are a couple things to note here:
As pointed out in this SO question, DataTable.Merge
does not change row state, so looking for rows with DataRowState.Added
will always come up empty.
Also, Merge
adds every row from the second table to the first, so everything from the second table would have come up even if the RowState had changed.
Finally, supposing we only added unique values from the second datatable to the first, we'd only get back the elements that were in the second set, but not in the first. Perhaps that is what you wanted, but I'll delineate both options in case you really want either one.
The Tools We'll Need
As found in other SO questions here and here, we can find the right set of entries by using any of the LINQ Set Operations
:
You can explore each of these further in MSDN, but just as an FYI, as mentioned in this SO Question, Union
and Concat
will both append an enumerable onto another, but Union will remove duplicates (when checking the equality of DataRow
s, you must pass in the overloaded IEqualityComparer
= DataRowComparer.Default
)
Also, we'll want to Enumerate over a DataTable, so to do this we'll need to call the AsEnumerable
method from the DataTableExtensions
namespace.
Solution Setup
Just so we're looking at the same code, here are some basic DataTables on which we can perform set operations
<!-- language: lang-vb -->
Dim dt, dataTable1, dataTable2 As New DataTable
With dt.Columns
.Add("ID", GetType(Integer))
.Add("Name", GetType(String))
End With
dataTable1 = dt.Clone
dataTable2 = dt.Clone
With dataTable1.Rows
.Add(1, "Tod")
.Add(2, "Jim")
End With
With dataTable2.Rows
.Add(2, "Jim")
.Add(3, "Han")
End With
Solution Sets
So if you want just the new items in the second table that don't appear in the first table, then you want a Right Outer Join
:
If you want all the data that is different between the two sets, you want a Full Outer Join
that excludes an Inner Join
You can read more about different sets and the joins that create them in this Jeff Atwood post.
Code
If you want the first option, you can get the elements in and only in the the second set by calling Except
on the second Datatable
and passing in the first
Dim just2 As DataTable
just2 = dataTable2.AsEnumerable _
.Except(dataTable1.AsEnumerable, DataRowComparer.Default) _
.CopyToDataTable()
The output will be:
| ID | Name |
| 3 | Han |
If you want the unique values in each set, you'll have to call distinct twice and then join those two values. We won't need to pass in the DataRowComparer
to the Union
method because we've guaranteed that there are no duplicates in the two tables
Dim just1 As IEnumerable(Of DataRow)
Dim just2 As IEnumerable(Of DataRow)
Dim union As DataTable
'get rows that are only in table1
just1 = dataTable1.AsEnumerable.Except(dataTable2.AsEnumerable, DataRowComparer.Default)
'get rows that are only in table2
just2 = dataTable2.AsEnumerable.Except(dataTable1.AsEnumerable, DataRowComparer.Default)
'get all unique rows
union = just1.Union(just2).CopyToDataTable
Since we won't need the unique element sets individually, there's no need to call CopyToDataTable
just so we can cast back into an IEnumerable(Of DataRow)
with AsEnumerable
The output of union will be:
| ID | Name |
| 1 | Tod |
| 3 | Han |