C# SQLite error Insufficient parameters supplied to the command - c#

I get the error SQLite error Insufficient parameters supplied to the command when running an insert statement in my program. However, I've got it narrowed down to it will only occur if I try to insert data into two different tables in succession. Meaning I insert data into one and everything in the method is diposed of since I am using USING statements.
This error won't occur if I insert data into one table stop debugging, rebuild my solution and then insert into the other table.
Anyone have any ideas? Like I said everything is encapsulated in using statements so I don't know what is getting held in memory.
Here is my method:
public static void SQLiteTableINSERT(string tableName)
{
using (SQLiteConnection Conn = new SQLiteConnection(SQLiteConn.Conn))
{
using (SQLiteTransaction sqliteTrans = Conn.BeginTransaction())
{
using (SQLiteCommand cmd = Conn.CreateCommand())
{
cmd.CommandText = PrepareInsert(tableName);
for (int i = 0; i < LocalDataSet.LocalDs.Tables[0].Rows.Count; ++i)
{
foreach (DataColumn col in LocalDataSet.LocalDs.Tables[0].Columns)
{
string temp = LocalDataSet.LocalDs.Tables[0].Rows[i][col, DataRowVersion.Current].ToString();
cmd.Parameters.AddWithValue("#" + col.ColumnName, temp);
}
cmd.ExecuteNonQuery();
}
}
sqliteTrans.Commit();
}
}
SQLite.SQLiteConn.Conn.Close();
}
Any ideas would be great.
Thanks.

I think you need to be using the tableName in the loop, assuming that the tables don't have the same schema.
The command is prepared for the table with name tableName, but the params you are adding are always from Tables[0], try Tables[tablename]

Related

Procedure Returning Blank Data Table from ASP.NET

I'm having issues with a stored procedure I'm attempting to run from an ASP.NET console application.
This is how I'm attempting to get the results in the code:
public static DataTable PerformStoredProcedure(int PracticeID, string CommandName)
{
DataTable dt = new DataTable();
try
{
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.AppSettings["DbConnString"].ToString()))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = CommandName;
cmd.Parameters.AddWithValue("#practiceID", PracticeID);
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
da.Fill(dt);
}
}
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Rows[i].ItemArray.Length; j++)
{
Console.Write(dt.Rows[i].ItemArray[j] + ",");
}
Console.WriteLine();
}
}
}
After the procedure is run I get returned with a blank data set with no errors. However, when I run the procedure in SQL Server Management Studio I works just fine.
You may notice I don't use the new SqlCommand() constructor when initiating the SqlCommand cmd. That is because the query is enormous and takes a while to run so I figured it was a timeout issue.
Following the suggestion here, I made the following class in order to adjust the execution timeout limit.
class CommandFactory
{
public static int CommandTimeout
{
get
{
int commandTimeout = 0;
var configValue = ConfigurationManager.AppSettings["commandTimeout"];
if (int.TryParse(configValue, out commandTimeout))
return commandTimeout;
return 120;
}
}
Figured I'd include that just to avoid confusion about whether the SqlCommand was being initialized correctly. After that adjustment, I began getting the issue where the command returns a blank data table. I assumed it could have to do with the minimum capacity of the data table dt, so I adjusted that as you can see in the original code.
I've done additional research and made the suggested fixes that are suggested to no avail:
1. Set nocount on
2. Use data adapter instead of data reader
3. This is the most detailed answer. There are several suggestions made with in the forum but the eventual resolution has to do with views being used by the database as a default and blocking access to desired data. I have already confirmed with our DBA that we do not do this.
Like I mentioned before, the query encompassed in the stored procedure is enormous and I haven't included it here because of its sheer size and because this post is long enough as it is. Again, I did confirm that the procedure is working in SSMS.
I also haven't included an example result set because the data contains restricted information.
I appreciate any help you can give.
UPDATE:
I added another method that calls a stored procedure to see if the problem was particular to the particular procedure being called. That second stored procedure also returned a blank data table so the problem is not specific to either procedure. (I also confirmed the second procedure was working and returning rows in SSMS)
That led me to believe there was something wrong with the app.config file for that project. I then added a third method that called a stored procedure on another project in the solution. However, even that returned a blank data table.
Figured I'd provide clues as I happen upon them.
You're quite close. Here are a few pointers:
The SqlConnection is never opened. You need to call .Open() in order
for the connection to be established.
If you set
a breakpoint on the .Fill() line and hover over the DataTable
object, it will appear as if the DataTable is empty (as you've seen), but .Fill() actually fills the Rows of the DataTable for you
automatically (see MSDN). I bet that if you loop through the Rows, you'll find
that your query has actually been returning data all along.
You could use using statements which automatically call .Dispose() for you as well as close the SqlConnection.
Here's an updated version of your method:
public static DataTable getAllModifiedChargesToday(int practiceID)
{
DataTable dt = new DataTable();
try
{
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.AppSettings["DbConnString"].ToString()))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "ch_getAllModifiedChargesToday";
cmd.Parameters.AddWithValue("#practiceID", practiceID);
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
da.Fill(dt);
}
}
}
}
catch (Exception ex)
{
LogError(ex);
}
return dt;
}
And here's a test you can run to view the data returned:
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Rows[i].ItemArray.Length; j++)
{
Console.Write(dt.Rows[i].ItemArray[j] + ",");
}
Console.WriteLine();
}
Also, you mentioned setting a timeout... I usually set my in my connection string, like so:
"Server=SERVER;Database=DATABASE;User ID=USER;Password=PASSWORD;Trusted_Connection=False;Asynchronous Processing=true;Timeout=60;"
You don't need that extra CommandFactory class at all, and you don't need to set a MinimumCapacity (MSDN) unless you are really trying to optimize performance and have a guesstimate of how many records will be returned. This property isn't used very often.

SqlBulkCopy ColumnMapping Error

My goal is to copy generic tables from one database to another. I would like to have it copy the data as is and it would be fine to either delete whatever is in the table or to add to it with new columns if there are new columns. The only thing I may want to change is to add something for versioning which can be done in a seperate part of the query.
Opening the data no problem but when I try a bulk copy but it is failing. I have gone though several posts and the closest thing is this one:
SqlBulkCopy Insert with Identity Column
I removed the SqlBulkCopyOptions.KeepIdentity from my code but it still is throwing
"The given ColumnMapping does not match up with any column in the source or destination" error
I have tried playing with the SqlBulkCopyOptions but so far no luck.
Ideas?
public void BatchBulkCopy(string connectionString, DataTable dataTable, string DestinationTbl, int batchSize)
{
// Get the DataTable
DataTable dtInsertRows = dataTable;
using (SqlBulkCopy sbc = new SqlBulkCopy(connectionString))
{
sbc.DestinationTableName = DestinationTbl;
// Number of records to be processed in one go
sbc.BatchSize = batchSize;
// Finally write to server
sbc.WriteToServer(dtInsertRows);
}
}
If I could suggest another approach, I would have a look at the SMO (SQL Server Management Objects) library to perform such tasks.
You can find an interesting article here.
Using SMO, you can perform tasks in SQL Server, such a bulk copy, treating tables, columns and databases as objects.
Some time ago, I used SMO in a small open source application I developed, named SQLServerDatabaseCopy.
To copy the data from table to table, I created this code (the complete code is here):
foreach (Table table in Tables)
{
string columnsTable = GetListOfColumnsOfTable(table);
string bulkCopyStatement = "SELECT {3} FROM [{0}].[{1}].[{2}]";
bulkCopyStatement = String.Format(bulkCopyStatement, SourceDatabase.Name, table.Schema, table.Name, columnsTable);
using (SqlCommand selectCommand = new SqlCommand(bulkCopyStatement, connection))
{
LogFileManager.WriteToLogFile(bulkCopyStatement);
SqlDataReader dataReader = selectCommand.ExecuteReader();
using (SqlConnection destinationDatabaseConnection = new SqlConnection(destDatabaseConnString))
{
if (destinationDatabaseConnection.State == System.Data.ConnectionState.Closed)
{
destinationDatabaseConnection.Open();
}
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationDatabaseConnection))
{
bulkCopy.DestinationTableName = String.Format("[{0}].[{1}]", table.Schema, table.Name);
foreach (Column column in table.Columns)
{
//it's not needed to perfom a mapping for computed columns!
if (!column.Computed)
{
bulkCopy.ColumnMappings.Add(column.Name, column.Name);
}
}
try
{
bulkCopy.WriteToServer(dataReader);
LogFileManager.WriteToLogFile(String.Format("Bulk copy successful for table [{0}].[{1}]", table.Schema, table.Name));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
finally
{
//closing reader
dataReader.Close();
}
}
}
}
}
As you can see, you have to add the ColumnMappings to the BulkCopy object for each column, because you have to define which column of source table must be mapped to a column of destination table. This is the reason of your error that says: The given ColumnMapping does not match up with any column in the source or destination.
I would add some validation to this to check what columns your source and destination tables have in common.
This essentially queries the system views (I have assumed SQL Server but this will be easily adaptable for other DBMS), to get the column names in the destination table (excluding identity columns), iterates over these and if there is a match in the source table adds the column mapping.
public void BatchBulkCopy(string connectionString, DataTable dataTable, string DestinationTbl, int batchSize)
{
using (SqlBulkCopy sbc = new SqlBulkCopy(connectionString))
{
sbc.DestinationTableName = DestinationTbl;
string sql = "SELECT name FROM sys.columns WHERE is_identity = 0 AND object_id = OBJECT_ID(#table)";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("#table", DestinationTbl);
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var column = reader.GetString(0);
if (dataTable.Columns.Contains(column))
{
sbc.ColumnMappings.Add(column, column);
}
}
}
}
// Number of records to be processed in one go
sbc.BatchSize = batchSize;
// Finally write to server
sbc.WriteToServer(dataTable);
}
}
This could still get invalid cast errors as there is no data type check, but should get you started for a generic method.
You can add
sbc.ColumnMappings.Add(0, 0);
sbc.ColumnMappings.Add(1, 1);
sbc.ColumnMappings.Add(2, 2);
sbc.ColumnMappings.Add(3, 3);
sbc.ColumnMappings.Add(4, 4);
before executing
sbc.WriteToServer(dataTable);
Thank you !!

Understanding of nested SQL in C#

using (var connection = new SqlConnection(...))
{
string sql = "SELECT * FROM tableA";
using (var command = new SqlCommand(sql,connection))
{
using (var reader = command.ExecuteReader(...))
{
//***************Sample Start
string sql2 = "INSERT into tableB(column1) VALUES('"+reader["column1"]+"')";
using (var command2 = new SqlCommand(sql2,connection))
{
...
}
//***************Sample End
}
}
}
By using the above code snippet, I believe its the best practice to deal with SQL in C#. Now after I retrieve a list of records from tableA, for each of the row I would like to insert into tableB.
However, it's throwing an exception
There is already an open DataReader associated with this Command which must be closed first
I know this problem can be solved by creating another method and insert into the table from there, I'm wondering if there is any other way. Thanks for any input.
You need to use a different sql connection for the insert than for the select...
...but we can do even better. You can re-write this to be one sql statement, like so:
INSERT into tableB(column1)
SELECT column1 FROM tableA
And then run it all at once like this:
string sql = "INSERT into tableB(column1, column2) SELECT column1, #othervalue As column2 FROM tableA;";
using (var connection = new SqlConnection(...))
using (var command = new SqlCommand(sql,connection))
{
command.Paramters.Add("#othervalue", SqlDbType.NVarChar, 50).Value = "something";
connection.Open();
command.ExecuteNonQuery();
}
The single sql statement is typically much faster, and you end up with less code, too. I understand that this is likely a simplified example of your real query, but I promise you: you can re-write it all as one statement.
Additionally, sometimes you still want to do some client-side processing or display with the new records after the insert or update. In that case, you still only need to send one call to the database, but there will be two separate sql statements in that single call. The final code would look more like this:
string sql = "INSERT into tableB(column1, column2) SELECT column1, #othervalue As column2 FROM tableA;"
sql += "SELECT columnn1, #othervalue As column2 FROM tableA;";
using (var connection = new SqlConnection(...))
using (var command = new SqlCommand(sql,connection))
{
command.Paramters.Add("#othervalue", SqlDbType.NVarChar, 50).Value = "something";
connection.Open();
using (var reader = command.ExecuteReader() )
{
while (reader.Read() )
{
//...
}
}
}
And because someone else brought up MARS (multiple active result sets), I'll add that while this can work, I've had mixed results using it for inserts/updates. It seems to work best when everything that shares a connection is only doing reads.
As has been mentioned in comments, you need a separate database connection for the insert. Each connection can handle one active statement at a time, and you have two here - one for the SELECT, one (at a time) for the INSERT.
Try this for instance:
string srcqry = "SELECT * FROM tableA";
using (SqlConnection srccon = new SqlConnection(ConnectionString))
using (SqlCommand srccmd = new SqlCommand(srcqry, srccon))
{
srccon.Open();
using (SqlDataReader src = srccmd.ExecuteReader())
{
string insqry = "INSERT INTO tableB(column1) VALUES(#v1)";
// create new connection and command for insert:
using (SqlConnection inscon = new SqlConnection(ConnectionString))
using (SqlCommand inscmd = new SqlCommand(insqry, inscon))
{
inscmd.Parameters.Add("#v1", System.Data.SqlDbType.NVarChar, 80);
inscon.Open();
while (src.Read())
{
inscmd.Parameters["#v1"].Value = src["column1"];
inscmd.ExecuteNonQuery();
}
}
}
}
Using parameters solves the SQL Injection vulnerability. You should always do this rather than building the query string from raw user input, or from data that you're pulling from a database, or... well, always. Write some helper methods to make it easier if you like, just make sure you do it.
aside from a bad example, why not just simplify the query to
insert into TableB (column1) select column1 from TableA

Read records from one table , and write to another table. Exception occurs procedure or function has too many arguments specified?

I am trying to retrieve list of records from one table , and write to another table. I've used a simple query to retrieve the values to SqlDataReader,then load them to a DataTable. Using the DataTableReader , I am going through the entire data set which is Saved in DataTable. The problem is, while reading each and every record I am trying to insert those values to another table using a Stored Procedure.But it only insert the first row of values,and for the second row onward giving some Exception saying."procedure or function has too many arguments specified".
string ConStr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
SqlConnection NewCon = new SqlConnection(ConStr);
NewCon.Open();
SqlCommand NewCmd3 = NewCon.CreateCommand();
NewCmd3.CommandType = CommandType.Text;
NewCmd3.CommandText ="select * from dbo.Request_List where group_no ='" +group_no+ "'";
NewCon.Close();
NewCon.Open();
SqlDataReader dr = (SqlDataReader)NewCmd3.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
DataTableReader reader = new DataTableReader(dt);
NewCmd.Dispose();
NewCon.Close();
NewCon.Open();
SqlCommand NewCmdGrpReqSer = NewCon.CreateCommand();
NewCmdGrpReqSer.CommandType = CommandType.StoredProcedure;
NewCmdGrpReqSer.CommandText = "Voucher_Request_Connection";
if (reader.HasRows)
{
int request_no = 0;
while (reader.Read())
{
request_no = (int)reader["request_no"];
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{
NewCmdGrpReqSer.ExecuteNonQuery();
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
catch (Exception xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
NewCmdGrpReqSer.Dispose();
NewCon.Close();
}
Any Solutions ?
As #Sparky suggests, the problem is that you continue to add parameters to the insertion command. There are several other ways in which the code could be improved, however. These improvements would remove the need to clear the parameters and would help to make sure you don't leave disposable resources undisposed.
First - use the using statement for your disposable objects. This removes the need for the explicit Close (btw, only one of Close/Dispose is needed for the connection as I believe Dispose calls Close). Second, simply create a new command for each insertion. This will prevent complex logic around resetting the parameters and, possibly, handling error states for the command. Third, check the results of the insertion to make sure it succeeds. Fourth, explicitly catch a SqlException - you don't want to accidentally hide unexpected errors in your code. If it's necessary to make sure all exceptions don't bubble up, consider using multiple exception handlers and "doing the right thing" for each case - say logging with different error levels or categories, aborting the entire operation rather than just this insert, etc. Lastly, I would use better variable names. In particular, avoid appending numeric identifiers to generic variable names. This makes the code harder to understand, both for others and for yourself after you've let the code sit for awhile.
Here's my version. Note there are several other things that I might do such as make the string literals into appropriately named constants. Introduce a strongly-typed wrapper around the ConfigurationManager object to make testing easier. Remove the underscores from the variable names and use camelCase instead. Though those are more stylistic in nature, you might want to consider them as well.
var connectionString = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (var newConnection = new SqlConnection(connectionString))
{
newConnection.Open();
using (var selectCommand = newConnection.CreateCommand())
{
selectCommand.CommandType = CommandType.Text;
select.CommandText ="select request_no from dbo.Request_List where group_no = #groupNumber";
selectCommand.Parameters.AddWithValue("groupNumber", group_no);
using (dataReader = (SqlDataReader)newCommand.ExecuteReader())
{
while (reader.HasRows && reader.Read())
{
using (var insertCommand = newConnection.CreateCommand())
{
insertCommand.CommandType = CommandType.StoredProcedure;
insertCommand.CommandText = "Voucher_Request_Connection";
var request_no = (int)reader["request_no"];
insertCommand.Parameters.Add("#serial_no", serial_no);
insertCommand.Parameters.Add("#request_no", request_no);
try
{
if (insertCommand.ExecuteNonQuery() == 1)
{
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
else
{
MessageBox.Show("Connection was not updated " + request_no);
}
}
catch (SqlException xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
}
}
}
}
Try clearing your parameters each time...
while (reader.Read())
{
request_no = (int)reader["request_no"];
// Add this line
NewCmdGrpReqSer.Parameters.Clear();
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{

List<T> to database and back again

So im trying to write my list to a database and ive got some questions. First of all nothing seems to get saved into the database but theres no errors that would cause a crash sofar and I know for a fact that this piece of code has worked in the past for me:
public void saveToDb(int var1, string var2)
{
SqlCommand cmd = new SqlCommand("Insert into [table] (col1, col2) VALUES (#param1, #param2)", conn);
cmd.Parameters.AddWithValue("#param1", var1);
cmd.Parameters.AddWithValue("#param2", var2);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}
I have a three piece layer going on, ui, logic and DAL. First of all it didnt work with the database and the app.config files in the DAL class library so those were moved to the main class library and now its not whining about the database already existing and it finds my connection string.
To send the data into this method im doing it this way:
for (int i = 0; i < myList.Count; i++)
{
da.saveToDb(myList.val1, myList.val2);
}
Its not giving me an error in the code but nothing seems to be saved or is resetted when I stop the program but I dont know which but I can see the variable being passed correctly by printing them before doing the insert so im guessing the Db is resetted? Is it putting my db into the debug folder and flushing it everytime or what?
Another thing is last time I did this it was a form so there was always going to be just one insert at a time, right now my list of objects could contain anywhere from 1 to alot of objects and with this way I would be opening and closing the db connection once for each object in my list right? How do you do bulk inserts? Been reading about datasets but they all seem to be about reading from a database not writing so im not sure about that. I did read from a database in another project using Dataset and adapter so that shouldnt be a problem but how do I bulk send a list into a table? To make it abit more tricky I cant just convert the entire list because of 10 propertys 8 is going to go into one table and the remaining 2 is to go in a second table so I would need to loop them and add the respective property to the respective dataset (or what you use).
edit
Well ive now made some adjustments and im trying two methods and none of which work but neither gives an error either which is getting frustrating.
First method:
public void saveToDb(int val1, string val2)
{
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["[MyConnectionString]"].ConnectionString);
SqlCommand cmd = new SqlCommand("Insert into [table] (val1, val2) VALUES (#param1, #param2)", conn);
cmd.Parameters.AddWithValue("#param1", val1);
cmd.Parameters.AddWithValue("#param2", val2);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}
And the second I try to sqlbulkcopy a table:
public void SaveToDb()
{
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["[MyConnectionString]"].ConnectionString))
{
conn.Open();
using (SqlBulkCopy bulk = new SqlBulkCopy(conn))
{
bulk.ColumnMappings.Add("col1", "col1");
bulk.ColumnMappings.Add("col2", "col1");
bulk.DestinationTableName = "[table]";
System.Diagnostics.Debug.Print("Open");
bulk.WriteToServer(tab);
foreach (DataRow row in tab.Rows)
{
foreach (var item in row.ItemArray)
{
System.Diagnostics.Debug.Print(item.ToString());
}
}
System.Diagnostics.Debug.Print("sending");
}
System.Diagnostics.Debug.Print("closing");
}
}
I shouldnt have to map it since the table contains the exact same columns as the table with the same naming (upper/lower case) but it gets weird with the primary key which increments so I mapped it so it should add the increment automatically but nothing gets inserted, the print sure enough shows the values but if I comment out the mapping I get an error saying that null cant be assigned (which is true my table wont take nulls) but the value shouldnt be null since its there in the table?
It seems you forgot the index.
for (int i = 0; i < myList.Count; i++)
{
da.saveToDb(myList[i].val1, myList[i].val2);
}
I think you are missing the connection object.
var cn = new SqlConnection().
Then you call open on the connection object.
connection.command will give access to the command object.

Categories

Resources