I am trying to compare two datatable and return only rows which has changes. In the below code I am cloning dt2 into dt3 but it doesn't pick the rows with changes instead it puts everything in there. Pls suggest some options.
Even if a single cell value is modified then I need to pick that row. Both datatable with have same set of rows and column each time I compare.
Table 1 Table 2
ID Name ID Name
1 Mark 1 Mark
2 Spencer 2 Spencer
3 Ryan 3 George
Table 3 Expected Result:
ID Name
3 George
CODE:
DataTable dt3 = new DataTable();
// It will compare the 2nd table with the 1st table
var contacts = dt2.AsEnumerable().Except(dt1.AsEnumerable(), DataRowComparer.Default);
dt3 = dt2.Clone();
foreach (DataRow rows in contacts)
{
dt3.ImportRow(rows);
}
You do not need two tables for this purpose. Simply calling GetChanges() on the second DataTable will get you the last row that has changed. Make sure you haven't (directly or indirectly) called AcceptChanges() on the DataTable, otherwise GetChanges() won't return anything.
DataRow exposes a property named RowState that is set to Modified when one or more columns of it get changed. AcceptChanges() and RejectChanges() reset this flag back to Unmodified.
You can even get the original value (Ryan in your case) and current value (George in your case) of a column by calling the indexer of DataRow with the second parameter (RowVersion) set to Original and Current respectively.
Try this:
dt1.Select()
.Join(dt2.Select(), x => x["Id"], y => y["Id"], (x, y) => new { x, y })
.Where(z => z.x["Name"] != z.y["Name"])
.Select(z => z.y);
Here dt1 is Table 1 and dt2 is Table 2.
It tries to join tables by Id, and then filters out records with same Name.
Related
I have a problem.Let's explain this:
I create a DataTable 'DataT' variable and added row to this variable.SameTime i have datagridview. After i assingmented 'DataT' to DataSource property of DataGridView.
I get 'DataT' Rows.Count property.It's value 10.But this value decrease from 10 to 5 when i removed 5 Rows of DataGridView.
So why decrease my value i just remove Rows of DataGridView but i did not remove rows of 'DataT' although rows count of 'DataT' decrease from 10 to 5.
I have not solve this problem.
EDIT:
Yes i bound datatable.But same problem there is other state.I explain with a example:
DataTable Table1 = new DataTable();
DataTable Table2 = new DataTable();
DataRow Rows;
private void Form1_Load(.......)
{
Tablo1.Columns.Add("Column1");
Tablo1.Columns.Add("Column2");
Rows = Table1.NewRow();
Rows[0] = "Hello";
Rows[1] = "Word";
Table1.Rows.Add(Rows);
Rows = Table1.NewRow();
Rows[0] = "Hello";
Rows[1] = "Word";
Table1.Rows.Add(Rows);
Table2 = Table1;
datagridview1.DataSource = Table1;
//This datagridview1 has 2 Rows and Table1 has 2 Rows.
datagridview1.Rows.RemoveAt(0);
//I am Removing one Row of datagridview1.Not from Table1.
//But Automatic removing Rows from Table1.
//Result=datagridview1 has 1 Row and Table1 has 1 Row.Why do remove rows from Table1?
//Even Rows Remove from Table2 when i remove rows from datagridview1.
}
As Henk Holterman pointed out in comment there are two reasons making Table2 to change
since Table1 is used as DataSource for grid any changes in the grid will be reflected in it. This is expected behavior of GridView.
GridView has its own Rows collection that is updated when one sets DataSource. Any changes to Rows collection (like removing item) are applied to DataTable set as data source. As result datagridview1.Rows.RemoveAt(0); actually indirectly removes row from the object referenced by Table1.
Both of your variables (Table1 and Table2) point to the same object - so whenever anything happens to the DataTable (i.e. row removed by GridView) the change is visible when you check either of variables.
Following code performs "shallow copy" when you seem to expect complete clone similar to behavior of int:
Table2 = Table1;
After that assignment both Table1 and Table2 refer to DataTable created on DataTable Table1 = new DataTable(); line.
See more What is the difference between a deep copy and a shallow copy?
Is it possible to change order of rows in DataTable so for example the one with current index of 5 moves to place with index of 3, etc.?
I have this legacy, messy code where dropdown menu get it's values from DataTable, which get it's values from database. It is impossible to make changes in database, since it has too many columns and entries. My original though was to add new column in db and order by it's values, but this is going to be hard.
So since this is only matter of presentation to user I was thinking to just switch order of rows in that DataTable. Does someone knows the best way to do this in C#?
This is my current code:
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
foreach (DataRow row in result.Rows)
{
m_cboReasonCode.Properties.Items.Add(row["FLOKKUR"].ToString().Trim() + " - " + row["SKYRING"]);
}
For example I want to push row 2011 - Credit previously issued to the top of the DataTable.
SOLUTION:
For those who might have problems with ordering rows in DataTable and working with obsolete technology that doesn't supports Linq this might help:
DataRow firstSelectedRow = result.Rows[6];
DataRow firstNewRow = result.NewRow();
firstNewRow.ItemArray = firstSelectedRow.ItemArray; // copy data
result.Rows.Remove(firstSelectedRow);
result.Rows.InsertAt(firstNewRow, 0);
You have to clone row, remove it and insert it again with a new index. This code moves row with index 6 to first place in the DataTable.
If you really want randomness you could use Guid.NewGuid in LINQ's OrderBy:
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
var randomOrder = result.AsEnumerable().OrderBy(r => Guid.NewGuid());
foreach (DataRow row in randomOrder)
{
// ...
}
If you actually don't want randomness but you want specific values at the top, you can use:
var orderFlokkur2011 = result.AsEnumerable()
.OrderBy(r => r.Field<int>("FLOKKUR") == 2011 ? 0 : 1);
You can use linq to order rows:
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
foreach (DataRow row in result.Rows.OrderBy(x => x.ColumnName))
{
m_cboReasonCode.Properties.Items.Add(row["FLOKKUR"].ToString().Trim() + " - " + row["SKYRING"]);
}
To order by multiple columns:
result.Rows.OrderBy(x => x.ColumnName).ThenBy(x => x.OtherColumnName).ThenBy(x.YetAnotherOne)
To order by a specific value:
result.Rows.OrderBy(x => (x.ColumnName == 2001 or x.ColumnName == 2002) ? 0 : 1).ThenBy(x => x.ColumName)
You can use the above code to "pin" certain rows to the top, if you want more granular than that you can use a switch for example to sort specific values into sorted values of 1, 2, 3, 4 and use a higher number for the rest.
You can not change the order or delete a row in a foreach loop, you should create a new datatable and randomly add the rows to new datatable, you should also track the inserted rows not to duplicate
Use a DataView
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
DateView view = new DateView(result);
view.Sort = "FLOKKUR";
view.Filter = "... you can even apply an in memory filter here ..."
foreach (DataRowView row in view.Rows)
{
....
Every data table comes with a view DefaultView which you can use, this way you can apply the default sorting / filtering in your datalayer.
public DataTable GetMCCHABAKflokka(string tableName, string sort, string filter)
{
var result = GetMCCHABAKflokka(tableName);
result.DefaultView.Sort = sort;
result.DefaultView.Filter = filter;
return result;
}
// use like this
foreach (DataRowView row in result.DefaultView)
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.
Its kind of subjective question to ask but still i hope i will find help.
I was learning about merging two tables from database to a singe DataTable.Then i came accross the following block of code.
DataTable mdt = new DataTable();
mdt.Columns.Add("products");
mdt.Columns.Add("price");
for (int i = 0; i < A.Rows.Count; i++)
{
DataRow dr = mdt.NewRow();
dr["product"] = A.Rows[i]["product"].ToString();
dr["price"] = A.Rows[i]["price"].ToString();
//A is a DataTable
mdt.Rows.Add(dr);
}
I can understand that the datas are being added to the row of a Datatable.
This is what i understood:
The column product of DataTable is assigned a value by dr["product"].Correct me if i am wrong.But how is this A.Rows[i]["product"].ToString(); working.
This should help:
A = DataTable
A.Rows = Collection of DataRows in A
A.Rows[i] = i-th row from collection
A.Rows[i]["product"] = Column "product" in row (return type of expression is object)
So when you do dr["product"] = A.Rows[i]["product"].ToString();, you are assigning the value of the product column of the current row from datatable A to the product column in your new data row. Similarly for the price column.
Rows[i] represents the index to which the value is assigned.I mean for the first loop the value is 0,for second loop the value is 1.So for the firs loop the values of product and price are added to first row with index 0.Similarly for the second loop the values of product and price are added to the second row with index 1.
And for the second part ie ["product"].ToString(),its simply converting the value of product to string.
EDIT
Since A is a Datatable which is already filled.What we are doing with that statement is,we are taking the DataTables's i-th row from the collection,converting it to string and assigning it to the column "product" in the datarow.
The ["product"].ToString() Is taking the value of the cell with the column name product, and converting it to a string to be assigned to your new row.
I have datatable1 which has 11 columns and 100,000 rows.. I would like to do a check to see if the text in column one starts with "one" and if it does, add that row into the second datatable. I have done the below but yet still it does not work.. I get the error that the row belong to another table
foreach (DataRow r in queryDataTable.Rows)
{
if (r[0].ToString().StartsWith(queryString))
{
dt.ImportRow(r);
}
}
You cannot directly import the row of one table into another datatable. You need to create a new row and then copy the row.
Try this -
dt.Rows.Add(r.ItemArray)
Instead of your for loop, you may use LINQ to select those rows which StartsWith queryString and then you can use CopytoDataTable method to create a new table for the selected rows.
var NewTable = queryDataTable.AsEnumerable()
.Where(r => r.Field<string>(0).StartsWith(queryString))
.CopyToDataTable();
Remember to include using System.Linq; at the top.