In a Winforms application I have to log all changes in datagrid (datatable). In other words I want to to get all changes, since it has been loaded. For this I want to use Datatable.GetChanges(). I know, that with GetChanges() I get a datatable containing a copy of all rows in the original DataSet that have pending changes.
My question is now, if it is also possible to get more additional information of the changes. For example I want to know if a row has been added or deleted or only has been updated. If a row has been updated I also want to know which cells has been updated? Is there a way to get all this information quickly or does I have to do a deep comparison row by row with the original datatable?
Or is it better to use RowState to get all changes?
For the row addition/deletion check RowState
For each item in the row (aka the cell) check DataRowVersion
foreach (DataRow dr in dt.Rows)
{
if (dr.RowState == DataRowState.Modified)
{
var changedCols = new List<DataColumn>();
foreach (DataColumn dc in dt.Columns)
{
if (!dr[dc, DataRowVersion.Original].Equals(
dr[dc, DataRowVersion.Current])) /* skipped Proposed as indicated by a commenter */
{
changedCols.Add(dc);
}
}
// now handle the changedCols list
}
}
Each DataRow in the resulting DataTable will have its RowState property set, which tells you whether the row was added, deleted, or updated.
I do not believe individual cells provide update information, however - it's only tracked by row.
Related
Question:
How to empty specific column of numbers from datagridview? (i mean erase the values without header, not delete the column).
for example FROM THIS TO THAT, so i dont need to do it manually
How i created the datagridview:
i created a database table, with 5 ID's all of datatype "int", i connected the datagridview through DataSource, bindingsource. And when i put values in the table and press button:(button code below)
table1BindingSource2.EndEdit();
table1TableAdapter2.Update(database1DataSet8.Table1;
It will save the values into DataTable.
For example the first ID in the table is : Konduktivita1[µS_cm-1]
What i think is neccesery is to actually delete the values from DataTable but i dont how to do it.
It would be easier if you provided some kind of code, but let's try something like this.
var sourceList = dataGridName.ItemsSource as List<sqlTable>; //or whatever your data is called
foreach (var item in sourceList)
{
item.Konduktiva1 = null; // or 0
}
I have a datatable and we fetch values from one database, put them in a datatable and insert them into another database. I am using the execute query method of sql and stored procedures to insert data. If one row has a string or binary data truncated error can we identify this using c# and printing that row on console??
Basically, everything is fine in dt but when I insert it I will get exception. Can I get the detail row which is causing exception?
Can anyone guide me on how to proceed with this? I need to know the exact row which is causing the issue.
If you don't know which specific row is causing the error, you'll probably have to foreach loop through it.
foreach(DataRow row in yourDataTable.Rows) {
//Check for the issue.
}
This will loop through each DataRow in your table. You'll have to check each individual cell, but you can't foreach loop through it, you'll have to do it manually based on the row. For example, if you know each Column name, you can do:
if(row["whatever"]...) // you want to check for the issue here.
You can also do:
int len = yourDataTable.Columns.Count;
foreach(DataRow row in yourDataTable.Rows) {
for(int i = 0; i < len; i++) {
//Check based in row[i] for your problem.
}
}
That will loop through each row, then each cell in each row based on index. You'll have to do your comparison based on the type received, though, which you'll have to determine based on the contents of your DataTable.
I want to improve the performance of updating the rows of a DataTable. In one example, I need to operate on a DateTime column, converting the System.DateTime value to a home-grown wrapper class. Meaning that currently, I loop over the Rows collection, and set each column of each row in series.
An example of what I do now:
dt.Columns.Add(new DataColumn("fldDateCreated", typeof(TypedDateTime)));
foreach (DataRow row in dt.Rows)
{
if (!(row["~fldDateCreated"] is DBNull))
row["fldDateCreated"] = new TypedDateTime((DateTime)row["~fldDateCreated"]);
}
dt.Columns.Remove("~fldDateCreated");
I would love to do this in parallel, but as MSDN says and my tests prove, DataTable does not like this sort of thing.
They state (for both DataTable and DataRow):
This type is safe for multithreaded read operations. You must
synchronize any write operations.
I have tried alternatives such as capturing the row data into Object[], updating that in parallel, and then updating the DataTable from those results. However, the price of finding and updating each row appears to be far more expensive than the original loop.
Is there a way forward here? Can I process these row updates in some threaded/parallel fashion?
here you go,
// referencing
// System.Data.DataSetExtensions
// System.Linq.Parallel
dt.AsEnumerable().AsParalell()
.Where(row => !(row["~fldDateCreated"] is DBNull))
.ForAll(row =>
{
row.SetField(
"fldDateCreated",
new TypedDateTime((DateTime)row["~fldDateCreated"]));
});
Somewhat confused here, the following code causes changes in the DataTable when I would not expect it to do so. Is there no actual individual value change management inside of a DataTable? Or am I missing something?
var table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Rows.Add("Sample Name");
table.AcceptChanges();
table.Rows[0].SetField("Name", "Sample Name");
var changes = table.GetChanges();
// expecting null, actually returns changes
I would have expected internally the table to use some pre-built logic to determine whether or not the new value is indeed different from the current value.
Why is it done like this?
When you modify content of a row, it updates the DataRow.RowState property, which can hold UnChanged, Added, Modified etc. Later DataTable.GetChanges filter out rows based on that row status. It is irrespective of the value.
DataTable.GetChanges - MSDN
Gets a copy of the DataTable containing all changes made to it since
it was last loaded, or since AcceptChanges was called, filtered by
DataRowState.
Also see:
Row States and Row Versions - MSDN
ADO.NET manages rows in tables using row states and versions. A row
state indicates the status of a row; row versions maintain the values
stored in a row as it is modified, including current, original, and
default values. For example, after you have made a modification to a
column in a row, the row will have a row state of Modified, and two
row versions: Current, which contains the current row values, and
Original, which contains the row values before the column was
modified.
This is apparently a design feature as even calling the SetField() extension method with exactly the same object and then checking the field values in DataRowVersion.Original and DataRowVersion.Current you can find that they are still reference equal. There is no documentation describing a different behavior.
I think this problem is related to Reference Types, and my lack of understanding of these ...
So I have dynamically created ASP.Net Tables (as in Web.UI.WebControls.Table, not the database variety)
These can have anything from one row with one cell with text, to a whole series of nested tables and controls, depending on the clients.
I need to loop through each TableRow, if a certain condition is met then I copy that row to a 2nd Table object. Here's a simplified bit of the code.
Table xTblComplete = (passed in as parameter) // original & complete table
Table xTblTemp = new Table(); // gets built dynamically with specific rows
foreach (TableRow xThisRow in xTblComplete.Rows)
{
if (xThisRow.Cells.Count > 0)
{
if (certain condition met)
{
xTblTemp.Rows.Add(xThisRow);
}
}
}
Where I come unstuck is that the foreach (row in table.rows) throws an error when I try to add the TableRow to Table2. I get the error "Collection was modified; enumeration operation may not execute. "
This makes sense, in that I should be making a COPY of that Table Row to add.
Can anyone advise on how this is done? I've scanned MSDN and the forums for general copying-of-reference types, but they all seem to point to using ICloneable , which I believe I'm unable to do as this isn't my class.
Am hoping this is something small and fundamental I'm missing out on, thanks in advance.
You are iterating through the row collection using the for loop. You can't modify the collection while doing that, that's why you are getting the error message. That row is attached to that table. Period.
If you need a copy of that row get the values you are looking for cell by cell. Here is an example:
TableRow tempRow= new TableRow();
Table xTblTemp= new Table();
for (int i = 0; i < xTblComplete.Rows[0].Cells.Count - 1; i++)
{
TableCell cell = xTblComplete.Rows[0].Cells[i];
tempRow.Cells[i].Text = cell.Text;
}
xTblTemp.Rows.Add(tempRow);
Thanks Ulises, your answers were helpful, unfortunately the complexity of these tables procluded a simple loop & copy contents. By that I mean that there were cells that might possibly have had 5 levels of nested tables, with any number of web controls inside each. Yep, a CSS perfectionist would retch at the idea of so many nested tables, but it's what had to be done!!
In the end I utilized a while(bln) loop, examining xTblComplete.Rows[0] each time.
If it met the condition , I would copy it to xTmpTable, which also removed it from xTblComplete.
If it failed the condition, I would remove it myself ( xTblComplete.Rows.Remove(xTblComplete.Rows[0]);
This way Rows[0] would always be the next to process.
After each Loop I checked the bln for more rows to process, if none then the loop would exit.