DataRow.AcceptChanges won't keep the new RowState - c#

This test code is really straightforward
var addedRows1 = (securityDataTable.GetChanges(DataRowState.Added));
MessageBox.Show(addedRows1.Rows[1].RowState.ToString());
MessageBox.Show(addedRows1.Rows.Count.ToString());
addedRows1.Rows[1].AcceptChanges();
var addedRows2 = (securityDataTable.GetChanges(DataRowState.Added));
MessageBox.Show(addedRows2.Rows[1].RowState.ToString());
MessageBox.Show(addedRows2.Rows.Count.ToString());
The 4 MessageBox show, in order, the following messages:
Added
3
Added
3
I would expect the count to return 2 on the last message. Why isn't that the case and can this be fixed by any mean? Note: The DataTable is not linked to a table nor a particular data source.
EDIT: Note that the RowState is ok (set to Unchanged) if I don't requery the GetChanges() the second time

GetChanges returns a copy of the rows. Are you using a data adapter to fill your data table? MSDN recommends calling AcceptChanges on the DataAdapter'
private void UpdateDataTable(DataTable table,
OleDbDataAdapter myDataAdapter)
{
DataTable xDataTable = table.GetChanges();
// Check the DataTable for errors.
if (xDataTable.HasErrors)
{
// Insert code to resolve errors.
}
// After fixing errors, update the database with the DataAdapter
myDataAdapter.Update(xDataTable);
}
Edit
Since you are just using a datatable, you could create a query for the rows that are added and call AcceptChanges on that row:
DataRow[] addedRows = datatable.Select(null, null, DataViewRowState.Added);
foreach (DataRow _ddr in addedRows)
{
_ddr.AcceptChanges();
}

Related

DataAdapter_RowUpdated Event's row changes aren't reflected in DataSet and DataTable

My situation involves batch updates to individual tables in an SQLite database through ADO.NET objects. I use the DataAdapter.Update() method to push the changes which works well:
DataTable changes = dataset.Tables[table].GetChanges();
if (changes == null) return 0;
SQLiteCommandBuilder scb = new SQLiteCommandBuilder(adapter);
scb.ConflictOption = ConflictOption.CompareRowVersion;
int cnt = adapter.Update(changes);
return cnt;
However each time a record is inserted I also want the local DataSet tables to reflect with the newly inserted row id. For this I use the adapter_RowUpdated event :
static void adapter_RowUpdated(object sender,
System.Data.Common.RowUpdatedEventArgs e)
{
if (e.StatementType == StatementType.Insert)
{
SQLiteCommand cmd = new SQLiteCommand("select last_insert_rowid();", conn);
e.Row["id"] = cmd.ExecuteScalar();
}
}
The above fetches last_insert_rowid() because I'm able to see it when I debug by putting a breakpoint. However, the assignment statement to e.Row["id"] isn't working. The id change isn't reflected in my original DataSet and DataTable objects. For example when I test the following value (N refers to the specific row index), it still has a DBNull value. What is going wrong here? How can I ensure that the specific row which just got inserted is updated with its corresponding id field value?
dataset.Tables["projects"].row[N]["id"];
After a little experimenting, I found the solution to this myself.
As strange as it may sound but it looks like adapter.Update() requires a dataset along with the actual table name in order for this to work. I was passing the table object (DataTable.GetChanges()) so far which did the job of updating the database but failed only in this particular scenario. The moment I did that, the inserted id started reflecting in rows all over the dataset!
//int cnt = adapter.Update(changes); // doesn't work
int cnt = adapter.Update(dataset, tableName); // works perfectly!
edit
Lo and Behold! It even works when I just pass the table like this instead of entire dataset. It was only causing problem when I was just passing the changes table (got from dataset.Tables[tableName].GetChanges()).
int cnt = adapter.Update(dataset.Tables[tableName]); // works perfectly!

Why doesn't my SQLite database table update? (datatable update)

I have a c# application with an SQLite database. It has a table called "Results_to_Risks" which I am trying to update. I first remove some rows in my local datatable, after which I use my update table function to update the table in the database.
foreach(var row in temp)
{
allResToRisks.Rows.Remove(row);
}
_databaseController.UpdateTable(allResToRisks, "Results_to_Risks");
My database controller update table method:
// Updates an entire database table.
public void UpdateTable(DataTable datatable, string table, string condition = "")
{
using (SQLiteConnection dbConnection = new SQLiteConnection(conString))
{
using (var sqliteAdapter = new SQLiteDataAdapter("SELECT * FROM '" + #table + "'" + #condition, dbConnection))
{
dbConnection.Open();
using(var builder = new SQLiteCommandBuilder(sqliteAdapter))
{
sqliteAdapter.Update(datatable);
}
}
}
}
In this particular case, I remove just one row. The datatable ends up empty (Rows.Count == 0), because this was the only row in that datatable. So nothing goes wrong there. However my table in the database still has the one row inside it.
What am I doing wrong?
I saw this on the MDSN website:
It is important to understand the difference between deleting a row in a DataTable and removing the row. When you call the Remove or RemoveAt method, the row is removed immediately. Any corresponding rows in the back end data source will not be affected if you then pass the DataTable or DataSet to a DataAdapter and call Update. When you use the Delete method, the row remains in the DataTable and is marked for deletion. If you then pass the DataTable or DataSet to a DataAdapter and call Update, the corresponding row in the back end data source is deleted.
So i tried to use Delete() instead of Remove(). However, this made no difference.

Datatable values to datagridview, only showing one columns data

I have a datatable filled with a report from a web service. I am now trying to display the datatable in an datagridview. This is the code I use to build the datatable:
// Create DataTabe to handle the output
DataTable dt = new DataTable();
dt.Clear();
dt.Columns.Add("EmployeeFirstName");
dt.Columns.Add("EmployeeLastName");
dt.Columns.Add("DepartmentName");
dt.Columns.Add("DepartmentCode");
dt.Columns.Add("LocationName");
dt.Columns.Add("DivisionCode");
dt.Columns.Add("EarningName");
dt.Columns.Add("OTHours");
dt.Columns.Add("WorkDate")
Fill the new datatable:
foreach (ReportRow row in report.Rows)
{
dt.Rows.Add(string.Join(",", row.ColumnValues));
}
Then I try to bind the data in the datatable to the dataGridview:
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = dt;
dataGridView1.Refresh();
When I run the application it only displays the data from the first column in the datatable. Do I need a loop of sorts to work through the columns or am I just missing a step?
Yes that's cause you are adding only one value to your dt when you say dt.Rows.Add(string.Join(",", row.ColumnValues));. You should be doing something like below (assuming that ReportRow also has the columns with same names like "EmployeeFirstName" else change the names accordingly)
foreach (ReportRow row in report.Rows)
{
DataRow dr = dt.NewRow();
dr["EmployeeFirstName"] = row["EmployeeFirstName"];
dr["EmployeeLastName"] = row["EmployeeLastName"];
dr["DepartmentName"] = row["DepartmentName"];
//rest of the columns fill
//once all columns filled
dt.Rows.Add(dr);
}
dt.Rows.Add(string.Join(",", row.ColumnValues)); -> You can either add a single DataRow item or a array of objects.
From your call, you chose the later, you are adding a array of objects, except you are adding ONE SINGLE object.
string.Join(",", row.ColumnValues) is one object.
Well after sleeping I have found the issue with dropping it into an sql table... I didn't take into account that the export to a CSV and the addition of the " , " would affect the export to sql. Here is the modification of the lines of code that was the issue:
foreach (ReportRow row in report.Rows)
{
dt.Rows.Add(row.ColumnValues);
}
Thank you all for your responses!

Get Inserted Row after .Net Dataset Row Added

I have a .Net dataset and am adding a row to a table. This works and the record is saved to the database. How do I get the updated version of my row after the insert. Or, alternatively, how do I know the ID of the item that was added (so that I can then use it in a subsequent child table insert.
MyDataSet.ProjectRow r = dsMyDataSet.Projects.AddProjectRow(txtTitle.Text);
m_daProjects.Update(dsMyDataSet.Projects);
// What is the ID of the new item here?
If the column is an identity column you can find the new ID's in the inserted rows.
You: "thanks. which object maintains a list of inserted rows?"
You can use DataTable.GetChanges(DataRowState.Added) to get a DataTable with all DataRows which are going to be added. You need to use it before AcceptChanges was called. If i remember correctly TableAdapter.Update calls AcceptChanges at the end. Then you need to use it before m_daProjects.Update(dsMyDataSet.Projects):
DataTable addedRows = ds.modModel.GetChanges(DataRowState.Added);
MyDataSet.ProjectRow r = dsMyDataSet.Projects.AddProjectRow(txtTitle.Text);
m_daProjects.Update(dsMyDataSet.Projects);
now addedRows contains all DataRows with the new identity value in each row
foreach(DataRow addedRow in addedRows.Rows)
Console.WriteLine("New ID: {0}", addedRow.Field<int>("IdColumn"));
Update: However, in your case it's simpler. You have already the single row that you want to insert. So you dont need to call DataTable.GetChanges at all.
You can see the new identity value in the (typed DataRow) ProjectRow r after Update.
Thanks to Tim Schmelter. In the link he posted there's a reference to an article on Beth Massi's blog with a complete walkthrough of the solution. It worked for me.
http://blogs.msdn.com/bethmassi/archive/2009/05/14/using-tableadapters-to-insert-related-data-into-an-ms-access-database.aspx
The basic steps are:
1) Add RowUpdated event handler on the strongly typed table adapter. This event handler issues a new OleDBCommand to the database to retrieve ##Identity and then assigns the integer to the member column of the table.
public void _adapter_RowUpdated(dynamic sender, System.Data.OleDb.OleDbRowUpdatedEventArgs e)
{
HMUI.Classes.AccessIDHelper.SetPrimaryKey(this.Connection, e);
}
public static void SetPrimaryKey(OleDbConnection trans, OleDbRowUpdatedEventArgs e)
{
if (e.Status == System.Data.UpdateStatus.Continue && e.StatementType == System.Data.StatementType.Insert)
{
if (pk != null)
{
OleDbCommand cmdGetIdentity = new OleDbCommand("SELECT ##IDENTITY", trans);
// Execute the post-update query to fetch new ##Identity
e.Row.Table.Columns[pk(0)] = Convert.ToInt32(cmdGetIdentity.ExecuteScalar());
e.Row.AcceptChanges();
}
}
}
2) In the constructor of the form using the dataset and table adapter I attach the function in step 1 to the RowUpdated event on the table adapter's internal data adapter.
// Event to handle inserted records and retrieve the primary key ID
m_daDataSources.Adapter.RowUpdated += new System.Data.OleDb.OleDbRowUpdatedEventHandler(m_daDataSources._adapter_RowUpdated);

How to add new row to datagridview?

I have DataGridView filled with data from datasource (SQL). Now I want to a add new row, but I can't, because new data can't be added to bounded DataGridView...
I was trying to :
dataGridView1.Source = null;
dataGridView1.Rows.Add("1");
but it clears my previous data in table. How to do it, to add new row without deleting previous data?
When you set the DataSource property to null, you are essentially removing all data from the DataGridView (since it doesn't know what to bind to anymore).
You have two options here. The first is to update the underlying data source. Let's assume that it's a DataTable. In this case, you'd do something like:
DataTable dt = dataGridView1.Source as DataTable;
dt.Rows.Add(new object[] { ... });
And then the DataGridView will pick up on the changes (note that if you are not binding to something that doesn't implement the INotifyCollectionChanged interface, you'll have to call the ResetBindings method to get the grid to refresh).
The other option is to let the DataGridView manage the rows. You can do this by manually adding each item using the Add method on the DataGridViewRowCollection returned by the Rows property:
foreach (var item in source)
{
dataGridView1.Rows.Add("1", "2", "3", ...);
}
I wouldn't say the second solution is optimal, but it will work.
Finally, assuming you are binding to a DataTable (or some other materialization of the data from an underlying data source), this doesn't do anything about to updating underlying data source (that would be a separate question).
The short answer is, you don't.
When you set your DataSource to null, you've broken the link between your DataGridView and your data source, so its data won't be persisted. You can't add a row to a bound DataGridView because it's supposed to represent the state of the underlying DataSource; you're effectively asking .net to make your table out of sync with its backing store, defeating the purpose of databinding in the first place.
If you want to add a row to the backing store, you should be adding a row in the DataSource, not in your DataGridView.
maybe you want to do it manually and detailed? Something like this?
DataSet ds = new DataSet();
OleDbDataAdapter adapter = null;
adapter = new OleDbDataAdapter("SELECT * FROM WHERE", conn);
adapter.Fill(ds);
dataGridView1.ColumnCount = 5; //how many columns returns your SQL query? starts with 0
dataGridView1.Columns[0].Name = "COl-1";
dataGridView1.Columns[1].Name = "COl-2";
dataGridView1.Columns[2].Name = "COl-3";
dataGridView1.Columns[3].Name = "COl-4";
dataGridView1.Columns[4].Name = "COl-5";
DataTable dt = ds.Tables[0];
foreach (DataRow dr in dt.Rows)
{
dataGridView1.Rows.Add(
(dr["COL_HEADER_NAME1"].ToString()),
(dr["COL_HEADER_NAME2"].ToString()),
(dr["COL_HEADER_NAME3"].ToString()),
(dr["COL_HEADER_NAME4"].ToString()),
(dr["COL_HEADER_NAME5"].ToString()));
}
You just add rows by using add method of rows collection
me.datagridview1.rows.add("first","second","third");
You can add any amount of items with array collection.

Categories