Error with SqlDataAdapter Update - c#

Using C#, SQL Server 2016 - Trying to write a method to update every record in a dataset - a single field to a new value - using a DataAdapter object, and am getting the following error message:
Dynamic SQL generation is not supported against a SelectCommand that does not return any base table information.
Here is my code (a method on a class containing a DataSet TheDS, an SQLDataAdapter TheAdapter, and a DataTable TheDataTable):
public void updateDSField<T>(string fieldName, T newVal)
{
// Updates field in dataset
SqlCommandBuilder builder = new SqlCommandBuilder(TheAdapter);
var col = TheDataTable.Columns[fieldName];
foreach (DataRow row in TheDataTable.Rows) row[col] = newVal;
TheAdapter.UpdateCommand = builder.GetUpdateCommand();
TheAdapter.Update(TheDS);
}
I read something about the SqlCommandBuilder not being able to handle >1000 rows - is that correct? Either way, what is the correct way to perform this operation?

I needed to build my own command string, as the builder does not handle more than 1000 rows. I was worried that this would update the entire table at first, but this link
C# DataTable Update Multiple Lines
put my mind at ease. (Assuming also I know the table name, TheTableName).
public void updateDSField<T>(string fieldName, T newVal)
{
// updates field in dataset
var col = TheDataTable.Columns[fieldName];
foreach (DataRow row in TheDataTable.Rows) row[col] = newVal; // * this marks the rows to be update by the commandtext
// create adapter and update string
TheAdapter.UpdateCommand = TheConn.CreateCommand();
string newValString = newVal.ToString();
if (typeof(T) == typeof(string)) newValString = "'" + newValString + "'";
TheAdapter.UpdateCommand.CommandText = "UPDATE [" + TheTableName + "] SET [" + fieldName + "] =" + newValString;
TheAdapter.Update(TheDS); // this will only update those rows marked by step * above
}

Related

C#: Deleting row in DataTable does not apply to data bank

My task should be quite simple but after hours and hours I must admit I'm completely stuck!
I simply want to delete a datarow from a datatable. My datatable is a copy of the table in my current dataset in a SQLite databank. It is mandatory to use the table.row.Delete() method. I am aware that delete() just marks the row to be deleted upon table update.
Below is the code I'm currently using:
I retrieve my data via:
public DataTable GetTable(string tableName)
{
string connectionPath = dbVariables.ConnectionString;
try
{
SQLiteConnection myConnection = new SQLiteConnection(connectionPath);
myConnection.Open();
string cmdStr = "SELECT * FROM " + tableName;
DataTable myTable = new DataTable();
SQLiteDataAdapter myAdapter = new SQLiteDataAdapter(cmdStr, myConnection);
myAdapter.FillSchema(myTable, SchemaType.Source);
myTable.Columns[dbVariables.ClassesID].AutoIncrement = true;
myTable.Columns[dbVariables.ClassesID].AutoIncrementSeed = 1;
myTable.Columns[dbVariables.ClassesID].AutoIncrementStep = 1;
myAdapter.Fill(myTable);
myConnection.Close();
return myTable;
}
catch (SQLiteException e)
{
MessageBox.Show(e.ToString());
return null;
}
}
Here I manipulate my data:
if (myResult == DialogResult.Yes)
{
//killTable.AcceptChanges();
DataRow[] dr = killTable.Select("" + cmVariables.ClassName + " = '" + cmbClasses.Text + "'");
//First I need to evaluate the row index of the row I want to delete
string indexName = (killTable.Rows.IndexOf(dr[0])).ToString();
int i = Int32.Parse(indexName);
// And we are done - I got my row index
DataRow modifiedRow = killTable.Rows[i];
killTable.Rows[i].Delete();
//I inserted this messagebox just to see the rowstatus - and yes, it is marked as deleted on runtime...
MessageBox.Show(killTable.Rows[i].RowState);
// I refer to this in the text below
killTable.AcceptChanges();
killClass_Execution(killTable, cmbClasses.Text, ShortClassNm);
}
And at least the code to update my datatable back to the databank:
public void UpdateTable(string tableName, DataTable sourceTable, bool newOrEdit)
{
try
{
string connectionPath = dbVariables.ConnectionString;
//Connection erstellen --> der connectString gibt dabei den Pfad an.
SQLiteConnection myConnection = new SQLiteConnection(connectionPath);
myConnection.Open();
//Einen Befehls-String erstellen, der das UPDATE-Command auslöst
// UPDATE cm_ClassTest SET className = userEditInput WHERE className = 'oldClassName'
string myUpdateString = "SELECT * FROM " + tableName + "";
SQLiteDataAdapter myAdapter = new SQLiteDataAdapter(myUpdateString, myConnection);
SQLiteCommandBuilder comBuild = new SQLiteCommandBuilder(myAdapter);
myAdapter.DeleteCommand = comBuild.GetDeleteCommand(true);
myAdapter.UpdateCommand = comBuild.GetUpdateCommand(true);
myAdapter.InsertCommand = comBuild.GetInsertCommand(true);
myAdapter.Update(sourceTable);
myConnection.Close();
if (newOrEdit == true)
{
MessageBox.Show("Klasse erstellt!");
}
else
{
MessageBox.Show("Klasse aktualisiert!");
}
}
catch (SQLiteException e)
{
MessageBox.Show(e.ToString());
}
}
In the code block for manipulating data you will find the AcceptChanges() method. At this time, there may have been no other changes appeared to my datatable - so after app start, deleting a row may be the users first action.
Also: each entry in my dataset is unique (school classes that are labeled with a unique class name).
Any help will be highly appreciated!
Regards,
Aran
OK - so I succeeded (finally). Debugging showed no issues, except for AcceptChanges() which got a call but did not result in actually deleting the dataRow.
All I can think of right now is that my primary approach to it was part of the problem: I get my DataTable right from the DataBase, do all the manipulations with it, and then send it back to DB via Update (see my update method above). The SQLite CommandBuilder seems not capable of interpreting my DAtaRow which is by then flagged 'Deleted', but kind of rubber-stamps it to the end of the method without throwing an exception.
By doing so I tried the GetChanges-Method:
DataRow[] dr = killTable.Select("" + cmVariables.ClassName + " = '" + cmbClasses.Text + "'");
string indexName = (killTable.Rows.IndexOf(dr[0])).ToString();
int i = Int32.Parse(indexName);
killTable.Rows[i].Delete();
DataTable killItTable = killTable.GetChanges(DataRowState.Deleted);
killClass_Execution(killItTable, cmbClasses.Text, ShortClassNm);
Now it's working - the copy of killTable (killItTable - stupid I know, but first thing I do tomorrow morning is give it better names :) ) provides info for the sql commandbuilder which seems to be recognized by it.
Whatever it is - it now works.
I think this is not supposed to happen - if someone can come up with any good suggestions, on how to improve my approach, I shall be glad.
best regards

Get AutoNumber column index in Ms Access 2010/2013

(first sorry for my English):
I want to temporarily change auto-number column to int64 data type to import records from another database. After importing the records, I want to change it back to auto-number.
My Problem:
I try to use the table.Columns[i].AutoIncrement property to check if this column is auto-number and get its index so that I can change its datatype, but this property didn't work for me, it returned false for all columns.
I work with 2010/2013 Access database.
So I want to know what to do to get index of auto-number column?
You can use this approach
// Bogus query, we don't want any record, so add a always false condition
OleDbCommand cmd = new OleDbCommand("SELECT * FROM aTable where 1=2", con);
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataTable test = new DataTable();
da.FillSchema(test, SchemaType.Source);
for(int x = 0; x < test.Columns.Count; x++)
{
DataColumn dc = test.Columns[x];
Console.WriteLine("ColName = " + dc.ColumnName +
", at index " + x +
" IsAutoIncrement:" + dc.AutoIncrement);
}

How to present the data from multiple DataTables in a series of stacked data grids?

I have an undefined amount of DataTables. I get them from my DataBase, each DataTable stands for one Table in my DataBase, I dont use all Tables of the DataBase just the few I need (these were selectet earlier in the code) and not all columns (same like the tables).
Now my problem: I want to show them in a DataGrid one below the other with breaks between them for the tablename.
This is how i get my DataTables:
List<DBTable> selectedTbl = DBObject.SingDBObj.GetSelectedTables();
foreach (DBTable tbl in selectedTbl)
{
string cols = tbl.GetSelectedColumnNames();
string query = #"SELECT " + cols + " FROM [" + DBObject.SingDBObj.DataSource + "].[" + DBObject.SingDBObj.Database + "].[" + tbl.Schema + "].[" + tbl.Name + "];";
DataTable DTShow = DBObject.SingDBObj.ExecuteQuery(query);
}
dataGridShowColmns.DataContext = ??;
Is there an easy way to do this?
You maybe mean something like:
DataSet ds = new DataSet();
// dataset is here just initialized for demonstration, you would first
// get the tables from database and populate dataset
for (int i = 0; i < ds.Tables.Count; i++)
{
DataTable dt = ds.Tables[i];
foreach (DataRow dr in dt.Rows)
{
dataGridView1.Rows.Add(dr);
}
}
In SQL name to a dataset cannot be assigned, only way you can do it in c#/VB. like
Dataset.Table[0].Name = "MyTable";

Dataset to xml Null Values

I have the code below, where from 3 tables I take the data and write an xml.
I want write (when a record column has null value) the column on the xml with null value. For example if (Category_name == Null ) to write on the xml (Null) Right now the code skip the column and don’t even have this column on the xml.
string xmlFileData = "";
string[] tables = new string[] { "category", "company", "config" };
string query;
xmlFileData += "<MyXml>";
SqlConnection conn;
dbconnect obj;
obj = new dbconnect();//initailizing class object
for (int i = 0; i < tables.Length; i++)
{
string ifemptquery;
DataSet ds = new DataSet();
DataSet ds1 = new DataSet();
conn = obj.getConnection(); //calling connection function
ifemptquery = "SELECT * FROM " + tables[i] ";
SqlCommand cmd1 = new SqlCommand(ifemptquery, conn);
conn.Open();
SqlDataAdapter da1 = new SqlDataAdapter(cmd1);
DataTable dt1 = new DataTable();
da1.Fill(dt1);
conn.Close();
if (dt1.Rows.Count > 0)
{
query = "SELECT * FROM " + tables[i] ";
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds);
conn.Close();
conn.Dispose();
ds.DataSetName = tables[i];
string vartbname = tables[i];
string trimed_tbname = vartbname.Replace("_", "");
ds.Tables[0].TableName = trimed_tbname;
xmlFileData += ds.GetXml();
}
else
{
}
}
xmlFileData += "</MyXml>";
File.WriteAllText(Server.MapPath("~/xmlbackup/") + "Backup.xml", xmlFileData);
I have been searching the whole world for a solution of writing null fields to XML using DataSet.WriteXML(). The answer posted by Vlad is the one I also used in my project but I found that following works in a much more performance optimized way. I have created a function for your convenience. Change your dataset tables one after the other by calling the following function and replacing the tables.
private DataTable GetNullFilledDataTableForXML(DataTable dtSource)
{
// Create a target table with same structure as source and fields as strings
// We can change the column datatype as long as there is no data loaded
DataTable dtTarget = dtSource.Clone();
foreach (DataColumn col in dtTarget.Columns)
col.DataType = typeof(string);
// Start importing the source into target by ItemArray copying which
// is found to be reasonably fast for nulk operations. VS 2015 is reporting
// 500-525 milliseconds for loading 100,000 records x 10 columns
// after null conversion in every cell which may be usable in many
// circumstances.
// Machine config: i5 2nd Gen, 8 GB RAM, Windows 7 64bit, VS 2015 Update 1
int colCountInTarget = dtTarget.Columns.Count;
foreach (DataRow sourceRow in dtSource.Rows)
{
// Get a new row loaded with data from source row
DataRow targetRow = dtTarget.NewRow();
targetRow.ItemArray = sourceRow.ItemArray;
// Update DBNull.Values to empty string in the new (target) row
// We can safely assign empty string since the target table columns
// are all of string type
for (int ctr = 0; ctr < colCountInTarget; ctr++)
if (targetRow[ctr] == DBNull.Value)
targetRow[ctr] = String.Empty;
// Now add the null filled row to target datatable
dtTarget.Rows.Add(targetRow);
}
// Return the target datatable
return dtTarget;
}
Refer similar question here - dataSet.GetXml() doesn't return xml for null or blank columns
Apart from solutions mentioned there, you can also traverse through dataset and write XML using XmlTextWriter. This method is not recommended if you are dealing with huge data.

Cannot add DataTable into MS Access table

I have a DataTable with about 6000 rows created from a query to SQL Server DB. Now I try to add the data into a MS-Access table. Everything seems to work. No exceptions are raised but no data is a being added. Here is the code I'm using:
DataTable t = new DataTable();
// fill data table
// Create DB - works!
var Name = DateTime.Now.ToString("H_mm_ss");
string strAccessConnectionString = #"Provider = Microsoft.Jet.OLEDB.4.0;" +
#"Data Source=" + "C:\\chhenning\\" + Name + ".mdb;";
ADOX.CatalogClass Catalog = new ADOX.CatalogClass();
Catalog.Create(strAccessConnectionString);
OleDbConnection accessConnection = new OleDbConnection(strAccessConnectionString);
accessConnection.Open();
// Create Table - works!
OleDbCommand Command = accessConnection.CreateCommand();
Command.CommandText = "Create Table Test( "
+ " ID_ int not null "
+ " , Year_ int not null "
+ " , Value_ float not null )";
Command.ExecuteNonQuery();
// Add data into table - does not work!
var adapter = new OleDbDataAdapter();
adapter.SelectCommand = new OleDbCommand("select * from Test", accessConnection);
var cbr = new OleDbCommandBuilder(adapter);
cbr.GetInsertCommand();
var Rows = adapter.Update(t);
I have made sure that my DataTable has data in and that both the DataTable and MS-Access have the same columns with the same data types.
Can someone spot what wrong with code? What are the steps I do to investigate the problem further?
The adapter.Update(table) method works looking at the RowState of the rows in the DataTable.
If the rows have RowState == DataRowState.Unchanged, the method will not perform any update.
I suppose that you load the datatable from the SqlServer database without making any change and thus the RowState is Unchanged for every row. Try to loop on the Rows and call
foreach(DataRow r in t.Rows)
r.SetAdded();
This will force the RowState on each column to Added and then the InsertCommand on the DataAdapter will execute the insert

Categories