GetChanges from merged datatables returns null - c#

I have following code:
DataTable datTable3 = new DataTable();
datTable3 = datTable1.Clone();
datTable2.Merge(datTable1);
datTable3 = datTable2.GetChanges();
Want I want to do is: Compare DataTable1 with DataTable2 and when there are rows in DataTable1 which aren't in DataTable 2 then add these rows into a new DataTable(3). This code above gives me an empty DataTable3 each time although the rows in the first dt are not equal to the rows in my second dt. what am I doing wrong? Sorry if that question may be too easy but I'm using C# since a couple of months.
EDIT: I found this solution which doesn't work for me... Why?
DataTable datTable3 = new DataTable();
datTable3 = datTable1.Clone();
foreach (DataRow row in datTable1.Rows)
{
datTable3.ImportRow(row);
}
foreach (DataRow row in datTable3.Rows)
{
row.SetAdded();
}
datTable2.Merge(datTable3);
DataTable datTableFinal = datTable2.GetChanges(DataRowState.Added);
// shows me a datatable with again the values from datTable1
// even if they are already in datTable2!
datTable2.RejectChanges();
datTable1.RejectChanges();

The DataTable.GetChanges() method Gets a copy of the DataTable that contains all changes made to it since it was loaded or AcceptChanges was last called.
In other words, GetChanges() is dependent on the DataRow.RowState property. A DataTable.Merge() will either preserve their 'RowState' property, or reset it to 'Unchanged'.
This means that when you merge two DataTables with rows that have 'Unchanged' RowStates, the merged table will also contain 'Unchanged' rows and the DataTable.GetChanges method will return null or Nothing.
EDIT : You can always iterate through the DataTable to see what rows are added to the merged table. Something like
foreach(DataRow row in datTable2.Rows)
{
Console.WriteLine("--- Row ---"); // Print separator.
foreach (var item in row.ItemArray) // Loop over the items.
{
Console.Write("Item: "); // Print label.
Console.WriteLine(item); // Invokes ToString abstract method.
}
}

Iterate through and use LoadDataRow(object[] value, bool fAcceptChanges) :
foreach (DataRow row in MergeTable.Rows)
{
TargetTable.LoadDataRow(row.ItemArray, false);
}
var changes = TargetTable.GetChanges();
changes had the desired value when I tried this method.

Related

Add columns into a DataTable inside a loop

I'd like to know if there is a way to automatically add columns in a DataTable inside a foreach loop? That is what I mean with "automatically".
I had in mind something like this:
DataTable dt = new DataTable();
foreach (var item in model.Statistik)
{
dt.Columns.Add();
row = dt.NewRow();
row[//Name of column goes here] = // either item or item.property;
dt.Rows.Add(row);
}
This is much more efficient than explicitly name every column before the for each loop. Because, what happens if some property changes or gets deleted, etc.? This is therefore, something I wish to get rid of.
I don't think you understand how dt.Columns and dt.Rows are related. There is one set of Columns for a DataTable. Every Row in the DataTable has all the matching columns - you don't have unique Columns in each Row. So every time you do dt.Columns.Add you are adding a column to every Row, old or new.
Also, how do you expect Row[<name of column>] to work if you don't specify the name to the DataTable?

Update :Result is not transfered to the orginal dataset. Sorting Rows in Dataset using C#

Update: even though I have got the required result but when the the second function access the data table the value is still the same
It a sequential program with two functions in different classes. First sort and second replace function. So it should sort the value and other function should be able to retrieve the sorted table but when it retrieve the datatable it gives the unsorted table.
I have used acceptchanges() but it also give the same result.
The program is trying to sort the table according to the required field and the result is stored in Sorted table variable. I am trying to copy this to the original i-e sourceTables but it is not working and is adding another row instead of updating [As shown in below dig]. I have tried to copy whole table but it does not work and by adding rows it is not giving the required result. I have used different methods but I am not getting the required result.
List<DataTable> sourceTables = context.GetDataByTable(sourceTable.StringValue);
List<DataTable> targetTables = context.GetDataByTable(targetTable.StringValue, sourceTables.Count);
string orderDesc= orderField.StringValue + " DESC";
for (int i = 0; i < sourceTables.Count; i++)
{
DataView dv = sourceTables[i].DefaultView;
if (orderDirection.StringValue == OrderDirectionAsc)
{
// for Sorting in Ascending Order
dv.Sort = orderField.StringValue;
}
else
{
// for Sorting in Descending Order
dv.Sort = orderDesc;
}
DataTable sortedTable = dv.ToTable();
DataTable dttableNew = sortedTable.Clone();
//sourceTables[i] = sortedTable.Copy();
//targetTables[i] = dv.ToTable();
//targetTables[i] = sortedTable.Copy();
// foreach (DataRow dr in sortedTable.Rows)
//// targetTables[i].Rows.Add(dr.ItemArray);
//}
for (int j = 0; j < sourceTables[i].Rows.Count; j++)
{
if (sourceTable.GetValue().ToString() == targetTable.GetValue().ToString())
{
foreach (DataRow dr in sortedTable.Rows)
{
targetTables[i].Rows.Add(dr.ItemArray);
}
else
{
foreach (DataRow dr in sortedTable.Rows)
{
targetTables[i].Rows.Add(dr.ItemArray);
}
// targetTables[i] = sortedTable.Copy(); does not work
//foreach (DataRow drtableOld in sortedTable.Rows)
//{
// targetTables[i].ImportRow(drtableOld);
//}
Instead of replacing the first values it is adding more rows
any help would be appreciated
If any one have problem with duplicate data or the changes are only local and is not effecting the original data table. Remember to always use .ImportRow(dr) function to add rows to the table and if you use Tables[i].Rows.Add(dr.ItemArray); the changes will affect only the local table and not the original one. Use .clear to remove the old rows from the orginal table. The action done directly on the original function will only effect the rows. If it is done on the clone copy changes will nor affect the original table.
Here is the complete code
DataTable sortTable = dv.ToTable();
if (sTable.GetValue().ToString() == tTable.GetValue().ToString())
{
sTables[i].Clear();
foreach (DataRow dr in sortTable.Rows)
{
sTables[i].ImportRow(dr);
}
sTables[i].AcceptChanges();
}

Enumerate over DataTable, filter items, then revert to DataTable

I'd like to filter items in my DataTable by whether a column value is contained inside a string array by converting it to an IEnumerable<DataRow>, afterwards I'd like to re-convert it to DataTable since that's what my method has to return.
Here's my code so far:
string[] ids = /*Gets string array of IDs here*/
DataTable dt = /*Databasecall returning a DataTable here*/
IEnumerable<DataRow> ie = dt.AsEnumerable();
ie = ie.Where<DataRow>(row => ids.Contains(row["id"].ToString()));
/*At this point I've filtered out the entries I don't want, now how do I convert this back to a DataTable? The following does NOT work.*/
ie.CopyToDataTable(dt, System.Data.LoadOption.PreserveChanges);
return dt;
I would create an empty clone of the data table:
DataTable newTable = dt.Clone();
Then import the rows from the old table that match the filter:
foreach(DataRow row in ie)
{
newTable.ImportRow(row);
}
Assuming that you want to filter the rows in-place, that is the filtered rows should be returned in the same DataTable that was created through the original database query, you should first clear the DataTable.Rows collection. Then you should copy the filtered rows to an array and add them sequentially:
ie = ie.Where<DataRow>(row => ids.Contains(row["id"].ToString())).ToArray();
dt.Rows.Clear();
foreach (var row in ie)
{
dt.Rows.Add(row);
}
An alternative way to achieve this could be to simply iterate through the rows in the DataTable once and delete the ones that should be filtered out:
foreach (var row in dt.Rows)
{
if (ids.Contains(row["id"].ToString()) == false)
{
row.Delete();
}
}
dt.AcceptChanges();
Note that if the DataTable is part of a DataSet that is being used to update the database, all modifications made to the DataTable.Rows collection will be reflected in the corresponding database table during an update.

how to update a ADO.NET DataRow in DataTable with another DataRow?

I want to do this:
public void UpdateDataRowsToDataTable(string tableName, List<DataRow> rows)
{
foreach (DataRow row in rows)
{
// this method does not exist!!
_dataSet.Tables[tableName].Rows.Update(row);
}
}
I need a method that finds a row (maybe by primary key) and updates changed rows to the row in DataTable.
The only possibility what I know is to use Select (or Find) and then maybe iterate all columns and give them new values. Please tell me that this cannot be true!
Although the question is not quite clear it sounds like you have a group of rows from one table and want to update the equivalent rows in another datatable. If this is the case they you can just use the find method and manually update them as you suggested, or alternatively, add the new rows to another table and merge them (there are all sorts of options for merging two data tables). Merging however will just do the same thing under the hood (i.e. find by primary key and update the columns).
Another way would be to just replace the row and set its status to modified datarow.SetModified()
Or you can delete the old one and add the new one...
Could you use boxing to do that if your datatables are the same.
DestDataset.DestDataTable newChildRecords =
(DestDataset.DestDataTable)_dataset.Tables[tableName].GetChanges(DataRowState.Added);
If you are using dataAdapter...
foreach (DataRow row in rows)
{
Row.BeginEdit();
//Do your stuff
Row.EndEdit();
}
dataAdapter.Update(_dataSet, "yourTable");
Another option is to use the ItemArray property to update the DataRow instance. The only thing is that you still have to select the proper rows. Also, note that you have to change the whole ItemArray instead of its elements in order to have the modification reflected in the DataTable.
foreach (DataRow row in rows)
{
// Select the row
var rows = _dataSet.Tables[tableName].Select(string.Format("Table_ID = {0}", row["Table_ID"]));
// Update the row
if (0 < rows.Length)
rows[0].ItemArray = (object[])row.ItemArray.Clone();
}
Regardless of the way you chose to update the DataRow, you can put the whole thing in an extension method.

Delete row from datatable where first column does not contain (some string)

I have DataTable with the following columns:
ClientID date numberOfTransactions price
ClientID is of type string and I need to ensure that its contents include "A-" and "N6" for every value in the table.
I need to delete all rows from the DataTable where this first column (ClientID) does not contain both "A-" and "N6" (some totals and other unnecessary data). How can I select and delete these rows specifically from the DataTable?
I know this:
foreach (DataRow row in table.Rows) // Loop over the rows.
{
//Here should come part "if first column contains mentioned values
}
I also know this
If (string.Contains("A-") == true && string.Contains("N6") == true)
{
//Do something
}
I need help how to implement this for first column of each row.
Try this:
EDIT: Totally messed up that last line, so if you tried it, try it now that I made it not stupid. =)
List<int> IndicesToRemove = new List<int>();
DataTable table = new DataTable(); //Obviously, your table will already exist at this point
foreach (DataRow row in table.Rows)
{
if (!(row["ClientID"].ToString().Contains("A-") && row["ClientID"].ToString().Contains("N6")))
IndicesToRemove.Add(table.Rows.IndexOf(row));
}
IndicesToRemove.Sort();
for (int i = IndicesToRemove.Count - 1; i >= 0; i--) table.Rows.RemoveAt(IndicesToRemove[i]);
try using this,
assuming dt as your Datatabe object and ClientID as your first column (hence using ItemArray[0])
for(int i=0; i<dt.Rows.Count; i++)
{
temp = dt.Rows[i].ItemArray[0].ToString();
if (System.Text.RegularExpressions.Regex.IsMatch(temp, "A-", System.Text.RegularExpressions.RegexOptions.IgnoreCase) || System.Text.RegularExpressions.Regex.IsMatch(temp, "N6", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
dt.Rows.RemoveAt(i);
i--;
}
}
Simple and straight forward solution... hope it helps
this should be more efficient, both in lines of Code and Time, try this :)
for(int x=0; x<table.Rows.Count;)
{
if (!table.Rows[x].ItemArray[0].contains("A-") && !table.Rows[x].ItemArray[0].contains("N6"))
table.Rows.RemoveAt(x);
else x++;
}
Happy Coding
Preface: C.Barlow's existing answer is awesome, this is just another route someone could take.
This is one way to do it where you never have to loop all the way through the original table (by taking advantage of the DataTable.Select() method):
DataTable table = new DataTable(); // This would be your existing DataTable
// Grab only the rows that meet your criteria using the .Select() method
DataRow[] newRows = table.Select("ClientID LIKE '%A-%' AND ClientID LIKE '%N6%'");
// Create a new table with the same schema as your existing one.
DataTable newTable = table.Clone();
foreach (DataRow r in newRows)
{
// Dump the selected rows into the table.
newTable.LoadDataRow(r.ItemArray, true);
}
And now you have a DataTable with only the rows you want. If necessary, at this point you could clear out the original table and replace it with the contents of the new one:
table.Clear();
table = newTable.Copy();
Edit: I thought of a memory optimization last night, you can just overwrite the existing table once you have the rows you need, which avoids the need for the temporary table.
DataTable table = new DataTable(); // This would be your existing DataTable
// Grab only the rows that meet your criteria using the .Select() method
DataRow[] newRows = table.Select("ClientID LIKE '%A-%' AND ClientID LIKE '%N6%'");
// Clear out the old table
table.Clear();
foreach (DataRow r in newRows)
{
// Dump the selected rows into the table.
table.LoadDataRow(r.ItemArray, true);
}

Categories

Resources