Calling SetField on a DataRow with the same value causes changes? - c#

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.

Related

EPPlus LoadFromCollection always put values in one column

I want to set each element of collection in one row, but they are always added in one column, not row.
var headers = new List<string> {...};
var row = 4;
worksheet.Cells[row, 2, row, headers.Count()].LoadFromCollection(headers);
From code - I select Range in what I want to load collection. But it doesn't work as expected. Result in image (Dates should be in row as column headers).
Using EPPlus version 4.5.3.2
LoadFromCollection is supposed to work like that. Normally you pass it a collection of a complex type, then the properties of that type become the column headers, while each item in the collection is populated on a separate row.
Since you're passing a simple type, it's just like you passed a complex type with a single property. Every value goes on a different row.
If you instead want the items in your collection to be on the same row, just write a for loop to iterate over the items in the collection and add them.

Database changes are not pushing to server

I have a winforms application, that is using the datasource and dataset controls from the IDE
This is the code block i am using
dsParcelBatch.BC_cpo_PARCELRow pr = dsParcelBatch.BC_cpo_PARCEL.FindByISN(int.Parse(activeParcelID));
pr.BeginEdit();
pr.NODE_ISN = 6;
pr.EndEdit();
pr.AcceptChanges();
dsParcelBatch.AcceptChanges();
I can read the correct row in line 1, and it is populating the PR row with the correct values,
i call the beginedit, and the dsParcelBatch has not updated.
I change the value of the NODE_ISN to the new value, and it sticks
I close the edit, and accept the changes in the row.
I can look into the datasource (dsParcelBatch) and the changes are in there - YEA!
I call the dsParcelBatch, and the changed value is changed....
but when i view the database, the value is back to the original value (5)
What am i missing something?
Before calling AcceptChanges you need to use a DataAdapter to Update the database.
AcceptChanges only changes the state of the rows in the DataTable, not in the database.
MSDN:
When AcceptChanges is called, any DataRow object still in edit mode
successfully ends its edits. The DataRowState also changes: all Added
and Modified rows become Unchanged, and Deleted rows are removed.

How to make all columns allow null before adding a new row?

How to make all columns allow null before adding a new row to the datatable .
dt.Rows.Add(dt.NewRow());
This line throws an exception
Column XXX does not allow nulls
How to fix this problem.
You don't add the row until it's filled and ready to be saved.
DataRow row = dt.NewRow();
...fill row with values...
dt.Rows.Add(row);
Usually the database designer specifies that a column can't be null for a reason. For example, it might be a primary key or a foreign key, or is otherwise mandatory information.
If you are sure that it is OK to provide no data for this column for this particular record, try passing an empty string.
It is usually better to initialize the whole row in memory before sending it to the database (instead of filling it field-by-field when it is already there).
However, if you absolutely must do that, and if your DBMS supports it, you can declare your NOT NULL constraints as deferred, so they are not checked until the transaction commits. Here is an Oracle example.
It's rarely good solution. Start from redesigning your database.
Consider removing NOT NULL constraints from all not necessary fields.
Also if any fields is obligatory, but you still do not want to fill it during row creation, set default value either in database or middle layer (ORM or whatever)
edit:
however, in this case it looks like you're just trying to pass an empty row to db, before initializing it with data. That will never work ;-)

Storing DataRows independently of a DataTable - RowNotInTableException

I'm creating a HashMap mapping the ID field of a row in a DataTable to the row itself, to improve lookup time for some frequently accessed tables. Now, from time to time, I'm getting the RowNotInTableException:
This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.
After looking around the net a bit, it seems that DataRows don't like not being attached to a DataTable. Even though the DataTable stays in memory (not sure if the DataRows keep a reference to it, but I'm definitely still caching it anyway), is it possible I'm breaking something by keeping those rows all isolated in a HashMap? What other reason can there be for this error? This post
RowNotInTableException when accessing second time
discusses a similar problem but there's no solution either.
UPDATE
I'm actually storing DataRowViews if that makes any difference.
The DataRow should be always attached to some DataTable. Even if is removed from DataTable, the row still has reference to the table.
The reason is, the schema of table is placed in DataTable not in DataRow (and the data itself too).
If you want fast lookup without DataTables, use some own structure instead of DataRow.

Is there a more efficient way to create lists that are the same?

I have a series of lists and classes that implement a table of data. The basic classes are: Columns, Rows, and Cells. The Rows contains some ID information and list of Cells which contains the row's value for each column. Currently I create the rows in a cell with code like this
void CreateRow()
{
Row newRow = new Row();
newRow.ID = idInfo;
foreach (var Column in Columns)
{
newRow.Cells.Add(new Cell(Column.ID));
}
Rows.Add(newRow);
}
The works fine, but in some cases am calling CreateRow() 20,000 times and have 200+ columns. So I am wondering if there is a more efficient way to populate the cells since the cells in a certain column in each row are identical.
Any ideas?
Thanks,
Jerry
Currently you create unique Cell object for each position in your matrix - that's a lot of cells given your use case of 20.000 + rows.
One approach to be more efficient could be to not add the cells at all when you construct the matrix, but only when you try to get or set its value (i.e using Lazy<T>).
Assuming you set the value of a cell before retrieving it, you could then have a factory method for creating a cell with a value - make the Cell object immutable and when you are "creating" a Cell for which you already have another cell with an identical value, return that cell instead. This could reduce the total number of Cell objects significantly, of course there's more overhead since you need to check whether you have a cell of the same value already and need to call the factory method again if you need to update the value of a cell.
Then again all of this could not be worth it if you do not experience any memory/performance problems with your current approach - measuring performance is key here.
Isn't Columns a collection?
var Ids = Columns.Select(c => c.Id).ToArray();
var Names = Columns.Select(c => c.Name).ToArray();
etc. Except why do that if Columns is already a collection? For you could do Columns[index].Id
Or if you must have the code you outlined:
Row newRow = new Row();
newRow.ID = idInfo;
// presuming Cells is a List<>
newRow.Cells.AddRange(Columns.Select(c => new Cell(c.Id)));
Rows.Add(newRow);
Some suggestions (depends on what you are looking for)
Consider using (strongly typed) DataSet/DataTable
If using List and you know the size, set the capacity to avoid reallocation (new List(2000))
Use struct instead of class if it makes sense
Cache objects if it makes sense (instead of duplicating the same object over and over)
You're creating the cells anyways. So I gather that the question refers to when you will fill the cells with their values, which are always in each column for all rows.
I actually think that from a correctness point of view, it makes sense to have the data duplicated, since they are in effect separate instances of the same data.
That said, if it is not really data, but you just want to show a view-column with the same value for each row, and you just want it as a data column in order to ease showing it as a view-column, then in your property-get Row.Cells(Id) you can check the ID, and if it's one of those columns where the value is always the same, return that value, bypassing looking up your _Cells collection.
If the data is mostly the same and sometimes different, you may want to use 'default values' where if the Cell object does not exist, a default value for that column will be returned. This necessitates a GetValue() method on the row, though, if you want to avoid having the Cell object altogether for places where it is default.
If you don't care about #1, you can really make a single instance of whatever the value is, and reference it in your Cell's value. This is harder to do for a Value Type than for a Reference Type (definition here) but it can be done.
Lastly, is there any reason you're not using .NET's supplied DataTable and DataRow types? I'm sure the MS geeks programmed as much efficiency as they could into those.

Categories