How to reuse DataTable for multiple SqlDataAdapter inserts - c#

Given a DataTable loaded with some data how can it be reused to perform multiple inserts into different SQL Server database tables using different SqlDataAdapter.Update() methods?
After using the DataTable to insert records into table 1, a different SqlDataAdapter will not insert data into table 2 and Update() method always returns 0 without throwing an exception.
I tried using the Copy() and RejectChanges() methods of the DataTable but it never inserts data into table 2.
Some code:
var dataAdapter1 = new SqlDataAdapter
{
UpdateBatchSize = updateBatchSize,
InsertCommand = new SqlCommand(insertCommand1, dbConnection)
{
UpdatedRowSource = UpdateRowSource.None
}
};
dataAdapter1 .InsertCommand.Parameters.Add("#Id", SqlDbType.Int, 0, "Id");
dbConnection.Open();
dataAdapter1 .Update(dtSomeDataTable);
// This inserts data into table 1.
var dataAdapter2 = new SqlDataAdapter
{
UpdateBatchSize = updateBatchSize,
InsertCommand = new SqlCommand(insertCommand2, dbConnection)
{
UpdatedRowSource = UpdateRowSource.None
}
};
var count = dataAdapter2.Update(dtSomeDataTable);
//This does not insert any records (or throws an exception) and variable count is always 0;

Take a look at this MSDN post to understand the concept of DataRow.RowState: https://msdn.microsoft.com/en-us/library/ww3k31w0(v=vs.110).aspx
When you call Update on the first SqlAdapter, all rows are marked as Unchanged and subsequent Update calls won't consider them anymore.
You could try "touching" each row by reading a value from one of the columns and setting that same value. Maybe that will cause the RowState to switch to Modified so that the second Update operation acts on all rows again.

Maybe try calling SetAdded on each DataRow, before the 2nd call to Update ?
https://msdn.microsoft.com/en-us/library/system.data.datarow.setadded%28v=vs.110%29.aspx

Resolved by keeping the original DataTable unchanged and working on copies for each required update created using the DataTable.Copy() method... If a better answer is posted I'll accept that instead...

Related

Updating MySQL returns rows affected, but doesn't actually update the Database

I'm currently using Mono on Ubuntu with MonoDevelop, running with a DataTable matching a table in the database, and should be attempting to update it.
The code following uses a Dataset loaded from an XML file, which was created from a Dataset.WriteXML on another machine.
try
{
if(ds.Tables.Contains(s))
{
ds.Tables[s].AcceptChanges();
foreach(DataRow dr in ds.Tables[s].Rows)
dr.SetModified(); // Setting to modified so that it updates, rather than inserts, into the database
hc.Data.Database.Update(hc.Data.DataDictionary.GetTableInfo(s), ds.Tables[s]);
}
}
catch (Exception ex)
{
Log.WriteError(ex);
}
This is the code for inserting/updating into the database.
public override int SQLUpdate(DataTable dt, string tableName)
{
MySqlDataAdapter da = new MySqlDataAdapter();
try
{
int rowsChanged = 0;
int tStart = Environment.TickCount;
da.SelectCommand = new MySqlCommand("SELECT * FROM " + tableName);
da.SelectCommand.Connection = connection;
MySqlCommandBuilder cb = new MySqlCommandBuilder(da);
da.UpdateCommand = cb.GetUpdateCommand();
da.DeleteCommand = cb.GetDeleteCommand();
da.InsertCommand = cb.GetInsertCommand();
da.ContinueUpdateOnError = true;
da.AcceptChangesDuringUpdate = true;
rowsChanged = da.Update(dt);
Log.WriteVerbose("Tbl={0},Rows={1},tics={2},", dt.TableName, rowsChanged, Misc.Elapsed(tStart));
return rowsChanged;
catch (Exception ex)
{
Log.WriteError("{0}", ex.Message);
return -1
}
I'm trying the above code, and rowsChanged becomes 4183, the number of rows I'm editing. However, when I use HeidiSQL to check the database itself, it doesn't change anything at all.
Is there a step I'm missing?
Edit: Alternatively, being able to overwrite all rows in the database would work as well. This is a setup for updating remote computers using USB sticks, forcing it to match a source data table.
Edit 2: Added more code sample to show the source of the DT. The DataTable is prefilled in the calling function, and all rows have DataRow.SetModified(); applied.
Edit 3: Additional information. The Table is being filled with data from an XML file. Attempting fix suggested in comments.
Edit 4: Adding calling code, just in case.
Thank you for your help.
The simplest way which you may want to look into might be to TRUNCATE the destination table, then simply save the XML import to it (with AI off so it uses the imported ID if necessary). The only problem may be with the rights to do that. Otherwise...
What you are trying to do can almost be handled using the Merge method. However, it can't/won't know about deleted rows. Since the method is acting on DataTables, if a row was deleted in the master database, it will simply not exist in the XML extract (versus a RowState of Deleted). These can be weeded out with a loop.
Likewise, any new rows may get a different PK for an AI int. To prevent that, just use a simple non-AI PK in the destination db so it can accept any number.
The XML loading:
private DataTable LoadXMLToDT(string filename)
{
DataTable dt = new DataTable();
dt.ReadXml(filename);
return dt;
}
The merge code:
DataTable dtMaster = LoadXMLToDT(#"C:\Temp\dtsample.xml");
// just a debug monitor
var changes = dtMaster.GetChanges();
string SQL = "SELECT * FROM Destination";
using (MySqlConnection dbCon = new MySqlConnection(MySQLOtherDB))
{
dtSample = new DataTable();
daSample = new MySqlDataAdapter(SQL, dbCon);
MySqlCommandBuilder cb = new MySqlCommandBuilder(daSample);
daSample.UpdateCommand = cb.GetUpdateCommand();
daSample.DeleteCommand = cb.GetDeleteCommand();
daSample.InsertCommand = cb.GetInsertCommand();
daSample.FillSchema(dtSample, SchemaType.Source);
dbCon.Open();
// the destination table
daSample.Fill(dtSample);
// handle deleted rows
var drExisting = dtMaster.AsEnumerable()
.Select(x => x.Field<int>("Id"));
var drMasterDeleted = dtSample.AsEnumerable()
.Where( q => !drExisting.Contains(q.Field<int>("Id")));
// delete based on missing ID
foreach (DataRow dr in drMasterDeleted)
dr.Delete();
// merge the XML into the tbl read
dtSample.Merge(dtMaster,false, MissingSchemaAction.Add);
int rowsChanged = daSample.Update(dtSample);
}
For whatever reason, rowsChanged always reports as many changes as there are total rows. But changes from the Master/XML DataTable do flow thru to the other/destination table.
The delete code gets a list of existing IDs, then determines which rows needs to be deleted from the destination DataTable by whether the new XML table has a row with that ID or not. All the missing rows are deleted, then the tables are merged.
The key is dtSample.Merge(dtMaster,false, MissingSchemaAction.Add); which merges the data from dtMaster with dtSample. The false param is what allows the incoming XML changes to overwrite values in the other table (and eventually be saved to the db).
I have no idea whether some of the issues like non matching AI PKs is a big deal or not, but this seems to handle all that I could find. In reality, what you are trying to do is Database Synchronization. Although with one table, and just a few rows, the above should work.

How do I override the insert command of a MySQLdataadapter to use ON DUPLICATE KEY UPDATE with C#

My application is extracting data from one DB and inserting the results into a different DB using MySqlDataAdapters and DataSets. I created a unique key in the target DB and want to update values in cases where I recalculated them.
I am hoping to use the data adapter Update method to handle this since there is a variable number of rows to INSERT/UPSERT each time. I also want the update to be as fast as possible since this is a repeating process.
If I exclude the possibility of update and just do inserts my code is functional as follows:
... // dsDSErrors is the dataset returned from my initial query
MySqlCommand cmdLocal = new MySqlCommand(queryLocal, myLocal);
MySqlDataAdapter daLocal = new MySqlDataAdapter(cmdLocal);
daLocal.MissingSchemaAction = MissingSchemaAction.AddWithKey;
DataSet dsLocal = new DataSet();
daLocal.Fill(dsLocal);
foreach (DataRow drError in dsDSErrors.Tables[0].Rows)
{
DataRow curRow = dsLocal.Tables[0].NewRow();
foreach (DataColumn hdr in dsDSErrors.Tables[0].Columns)
curRow[hdr.ColumnName] = drError[hdr.ColumnName];
dsLocal.Tables[0].Rows.Add(curRow);
}
MySqlCommandBuilder bldDSErrors = new MySqlCommandBuilder(daLocal);
daLocal.Update(dsLocal);
I understand that I can use daLocal.InsertCommand = new MySqlCommand(insert command) but was wondering if it is necessary to then loop through the results and add them to the VALUES section of the insert statement or if there was a method I'm not aware of that will handle this.
Barring built in functionality, I assume its best to use string builder to build up the insert query with all my values rows?

updating the values in database from datagridview

i want to create a for loop in which i want to write a query which updates the the value of each row in database. the rows are present in the datagridview as well as in the database. the aim is when the changes are made in datagridview so using a button the change are also applied in the database table too. in each row the barcode and its quantity is different. if the changes are made in all the rows in datagridview so it is also to be applied in database using button and also please help with the parameters.
here is the query which should be considered in the for loop:
SqlCommand cmd2 = new SqlCommand("update prod_info set item_quantity=#qty where barcode=#barcode ", con);
consider barcode as column1 and item_quantity as column2.
so far to create a for loop i have tried this but getting error in the for loops:
for (int i = 0; dataGridView2.Rows.Count; i++) //getting error here
{
SqlCommand cmd2 = new SqlCommand("update prod_info set item_quantity=#qty where barcode=#barcode ", con);
cmd2.Parameters.AddWithValue("#barcode", dataGridView2.Rows[i].Cells[1].Value.ToString());
cmd2.Parameters.AddWithValue("#qty", dataGridView2.Rows[i].Cells[1].Value.ToString());
}
you should call cmd2.ExecuteNonQuery(); in your loop ... otherwise no sql commands will be executed
to get a better solution you should move to creation of cmd2 before the loop. also add the parameters there (without assigning values) ... inside the loop just assign the values and call ExecuteNonQuery.
maybe the best solution would be to use databinding and a SqlDataAdapter with assigned UpdateCommand.
just saw that there probably is an error in your code ... you use the value from Cell[1] for both of your parameters ...
example:
first create a DataTable ... var dt = new DataTable();
then add the columns you want in your grid to the DataTable ... dt.Columns.Add("xyz");
then attach the DataTable to your grid: dataGridView2.DataSource = dt;
now you should be able to edit the contents of column "xyz". to write the values to the database you create a SqlDataAdapter ... var adp = new SqlDataAdapter();
then set adp.InsertCommand = new SqlCommand(...) and adp.UpdateCommand = new SqlCommand(...)
now you can call adp.Update(); and all the values from your grid are written to db ... for newly added rows the InsertCommand is invoked and for edited rows the UpdateCommand is invoked.

Updating using DataTable in database table?

I have a table which has some 100-200 records.
I have fetch those records into a dataset.
Now i am looping through all the records using foreach
dataset.Tables[0].AsEnumerable()
I want to update a column for each record in the loop. How can i do this. Using the same dataset.
I'm Assumng your using a Data Adapter to Fill the Data Set, with a Select Command?
To edit the data in your Data Table and save changes back to your database you will require an Update Command for you Data Adapter. Something like this :-
SQLConnection connector = new SQLConnection(#"Your connection string");
SQLAdaptor Adaptor = new SQLAdaptor();
Updatecmd = new sqlDbCommand("UPDATE YOURTABLE SET FIELD1= #FIELD1, FIELD2= #FIELD2 WHERE ID = #ID", connector);
You will also need to Add Parameters for the fields :-
Updatecmd.Parameters.Add("#FIELD1", SQLDbType.VarCHar, 8, "FIELD1");
Updatecmd.Parameters.Add("#FIELD2", SQLDbType.VarCHar, 8, "FIELD2");
var param = Updatecmd.Parameters.Add("#ID", SqlDbType.Interger, 6, "ID");
param.SourceVersion = DataRowVersion.Original;
Once you have created an Update Command with the correct SQL statement, and added the parameters, you need to assign this as the Insert Command for you Data Adapter :-
Adaptor.UpdateCommand = Updatecmd;
You will need to read up on doing this yourself, go through some examples, this is a rough guide.
The next step is to Enumerate through your data table, you dont need LINQ, you can do this :-
foreach(DataRow row in Dataset.Tables[0].Rows)
{
row["YourColumn"] = YOURVALUE;
}
One this is finished, you need to call the Update() method of yout Data Adapter like so :-
DataAdapter.Update(dataset.Tables[0]);
What happens here, is the Data Adapter calls the Update command and saves the changes back to the database.
Please Note, If wish to ADD new rows to the Database, you will require am INSERT Command for the Data Adapter.
This is very roughly coded out, from the top of my head, so the syntax may be slightly out. But will hopefully help.
You should use original DataAdapter (adapter in code below) that was used to fill DataSet and call Update method, you will need CommandBuilder also, it depends what DB you are using, here is the example for SQL server :
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.UpdateCommand = builder.GetUpdateCommand();
adapter.Update(dataset);
dataset.AcceptChanges();
Here is the good example :
http://support.microsoft.com/kb/307587
The steps would be something like:
- create a new DataColumn[^]
- add it to the data table's Columns[^] collection
- Create a DataRow [^] for example using NewRow[^]
- Modify the values of the row as per needed using Item[^] indexer
- add the row to Rows[^] collection
- after succesful modifications AcceptChanges[^]
Like this:
DataTable table = new DataTable();
table.Columns.Add("ID", typeof(int));
table.Columns.Add("ProductName");
table.Rows.Add(1, "Chai");
table.Rows.Add(2, "Queso Cabrales");
table.Rows.Add(3, "Tofu");
EnumerableRowCollection<DataRow> Rows = table.AsEnumerable();
foreach (DataRow Row in Rows)
Row["ID"] = (int)Row["ID"] * 2;
Add the column like below.
dataset.Tables[0].Columns.Add(new DataColumn ("columnname"));
Update the columns values like below.
for (int i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
dataset.Tables[0].Rows[i]["columnname"] = "new value here";
}
Update Database
dataset.AcceptChanges();

DataAdapter.Update() does not update data in DB

I've got an assignment which requires me to update the northwind database,
I've done everything like the tutorials say as follows
I fill The DataTable Using The DataAdapter.Fill(table).
I build the Delete,Insert,Update Commands using CommangBuilder
SqlDataAdapter adapter = new SqlDataAdapter(selectStr, conn);
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.DeleteCommand = builder.GetDeleteCommand(true);
adapter.UpdateCommand = builder.GetUpdateCommand(true);
adapter.InsertCommand = builder.GetInsertCommand(true);
adapter.Fill(employees_table);
I also set a primary key for the table:
DataColumn[] employees_keys = new DataColumn[2];
employees_keys[0] = employees.Columns["EmployeeID"];
employees_table.PrimaryKey = employees_keys;
Now I've attempted to delete and add a row:
// accepts an employee object and creates a new new row with the appropriate values for
// an employee table row
DataRow row = ConvertEmployeeToRow(employeeToAdd);
employee_table.Rows.Add(row);`
and deleting a row:
DataRow row = employees.Rows.Find(employeeToDismiss.ID);
employees.Rows.Remove(row);
I should also point out that I've attempted to use row.SetAdded() and row.Delete()
Anyway, at the end when I try to update the database
int k = employees_adapter.Update(employees_table);
on added rows sometimes k get valued, on remove never, and in either case nothing really gets updated at all in the database itself.
Any insight of what I'm doing wrong?
Make sure you're calling employee_table.AcceptChanges() after the call to Update() to save the changes to the database.
Check if the CommandBuilder really makes an Update command for you, like this:
MessageBox.Show(adapter.UpdateCommand.CommandText);
If the primary key info is missing it won't build the update command at all!
I am little confuse here your SqlDataAdapter name is adapter and you are doing updation in employees_adapter.The steps are so simple to work with SqlDataAdapter just follow theses
Step No 1:
SqlDataAdapter adapter = new SqlDataAdapter(selectStr, conn);
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
DataTable employees_table= new DataTable ();
adapter.Fill(employees_table);
Step No 2:
After you got the data in dataset start manipulation on it..
To insert :
DataRow MyRow = employees_table.NewRow();
//Now Fill data in MyRow
employees_table.Tables[0].Rows.Add(MyRow);
employees_table.AcceptChanges();
adapter.Update(employees_table);
To Delete :
/get only the rows you want
DataRow[] result = employees_table.Select("ID ="+id); //id will be provided by you
//Now do here data updation
employees_table.AcceptChanges()
adapter.Update(employees_table);
Like wise you can apply updation..But after doing any changes must call
table.AcceptChanges() and then'adapter.Update(employees_table)'

Categories

Resources