I've recently started learning some databases things in .NET (C#) and I have a problem with inserting list of objects into local database. I don't know the reason but, after executing query, base is still empty and there is no error or warning, can you tell me if there is any problem with the code, or is there any other reason why it does not work properly.
I've tried to debug, but code seems to work, it passes if statement, and adds parameter's value, I've also removed thread start method and did it synchronously but still nothing.
public static void SaveData()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
using (SqlConnection conn = new SqlConnection(conString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("INSERT INTO Przestoje(Urzadzenie, Czas, Data) VALUES(#nazwa, #czas, #data)", conn))
{
cmd.Parameters.AddWithValue("#nazwa", SqlDbType.NVarChar);
cmd.Parameters.AddWithValue("#czas", SqlDbType.Int);
cmd.Parameters.AddWithValue("#data", SqlDbType.NVarChar);
int count = allEncounters.Count;
for (int i = 0; i < count; i++)
{
if (i >= NextIndex)
{
cmd.Parameters["#nazwa"].Value = allEncounters[i].Name;
cmd.Parameters["#czas"].Value = allEncounters[i].timeOnTimeout * 10;
cmd.Parameters["#data"].Value = allEncounters[i].startDate.ToString();
}
}
NextIndex = count;
}
}
}).Start();
}
At some point you have to actually execute the SQL command, which you currently don't do:
cmd.ExecuteNonQuery();
If you're looking to insert one record, this would be near the end. Though it looks like you're trying to insert multiple records in a loop, so this would of course happen inside the loop. (Once per record.)
You have to execute the SqlCommand. Put this before "NextIndex = count;":
cmd.ExecuteNonQuery();
You forgot to run in using block:
cmd.ExecuteNonQuery();
You can trim that down
for (int i = NextIndex; i < allEncounters.Count; i++)
{
cmd.Parameters["#nazwa"].Value = allEncounters[i].Name;
cmd.Parameters["#czas"].Value = allEncounters[i].timeOnTimeout * 10;
cmd.Parameters["#data"].Value = allEncounters[i].startDate.ToString();
cmd.ExecuteNonQuery();
}
NextIndex = allEncounters.Count;
Related
I have been querying and inserting data from and to Impala via ODBC, but it is slow (at least compared to a Postgres or SQL Server) and ODBC driver makes possible to execute queries one by one, which is absolutely not recommended as every insert creates a new file in HDFS.
I read through ODBC docs available on Simba site and Cloudera site, but batch operations or direct SQL execution is not mentioned.
This is the code I tried so far
static void Main(string[] args)
{
string insert = $"INSERT INTO tbl(version, snapshot) " +
$"VALUES(?, ?)";
OdbcConnection connection = new OdbcConnection("DSN=connection");
connection.Open();
using (OdbcCommand insertCommand = new OdbcCommand(insert, connection))
{
for (int i = 10; i < 15; i++)
{
List<OdbcParameter> parameters = new List<OdbcParameter>();
OdbcParameter versionParam = new OdbcParameter("#version", OdbcType.Text);
versionParam.Value = "bla" + i;
parameters.Add(versionParam);
OdbcParameter snapshotParam = new OdbcParameter("#snapshot", OdbcType.Text);
snapshotParam.Value = "blabla" + i;
parameters.Add(snapshotParam);
insertCommand.Parameters.AddRange(parameters.ToArray());
}
string query = insertCommand.CommandText.ToString();
Console.WriteLine(query);
//insertCommand.ExecuteReader();
insertCommand.ExecuteNonQuery();
}
}
A single row is inserted however 5 should be. What I'm doing wrong?
The .ExecuteNonQuery() call needs to be inside the for (....) loop ..... on the other hand, the code to create the parameters must be outside the for () loop - inside the loop, you should only set the parameter's values - not keep re-creating them over and over again ....
Try this code:
static void Main(string[] args)
{
string insert = $"INSERT INTO tbl(version, snapshot) VALUES(?, ?)";
OdbcConnection connection = new OdbcConnection("DSN=connection");
connection.Open();
using (OdbcCommand insertCommand = new OdbcCommand(insert, connection))
{
// *create* the parameters *outside* the "for" loop - only once!
List<OdbcParameter> parameters = new List<OdbcParameter>();
OdbcParameter versionParam = new OdbcParameter("#version", OdbcType.Text);
parameters.Add(versionParam);
OdbcParameter snapshotParam = new OdbcParameter("#snapshot", OdbcType.Text);
parameters.Add(snapshotParam);
insertCommand.Parameters.AddRange(parameters.ToArray());
for (int i = 10; i < 15; i++)
{
// inside the "for" loop - only set the values of the parameters
versionParam.Value = "bla" + i;
snapshotParam.Value = "blabla" + i;
// ... and then *execute* the query to run the insert!
string query = insertCommand.CommandText.ToString();
Console.WriteLine(query);
insertCommand.ExecuteNonQuery();
}
}
}
I'm getting the following error:
Index was outside the bounds of the array
The for loop code:
for (int i = 0; i < listEquipment.Items.Count - 1; i++)
{
SqlCommand cmd = new SqlCommand("INSERT INTO [dbo].[EquipmentItems] ([RequestID], [TypeID]) VALUES (#RequestID, #TypeID)", conn);
cmd.Parameters.Add("#RequestID", SqlDbType.Int).Value = userRequest;
cmd.Parameters.Add("#TypeID", SqlDbType.Int).Value = (listEquipment.SelectedItems[i] as Equipment).equipmentID;
cmd.ExecuteNonQuery();
}
The line with the error:
cmd.Parameters.Add("#TypeID", SqlDbType.Int).Value = (listEquipment.SelectedItems[i] as Equipment).equipmentID;
Please help! I'm new to programming.
You loop all items and then access that index in listEquipment.SelectedItems[i]. But there are probably not all items selected which means that this collection is smaller and you can't access it.
So i assume that you dont want to loop all items but only the selected, so use:
for (int i = 0; i < listEquipment.SelectedItems.Count; i++)
{
}
change
for (int i = 0; i < listEquipment.Items.Count - 1; i++)
to
for (int i = 0; i < listEquipment.SelectedItems.Count; i++)
You are looking for something like this (let me expailn via comments in the code below):
//DONE: make sql readable
string sql =
#"INSERT INTO [dbo].[EquipmentItems] (
[RequestID],
[TypeID])
VALUES (
#RequestID,
#TypeID)";
// Create command once - use many (within the loop)
//DONE: wrap IDisposable into using
using (SqlCommand cmd = new SqlCommand(sql, conn)) {
// user request is the same for all the equipment
cmd.Parameters.Add("#RequestID", SqlDbType.Int).Value = userRequest;
cmd.Parameters.Add("#TypeID", SqlDbType.Int);
//DONE: foreach + OfType<> is by far more readable in the context:
// "for each selected equipment execute a query"
foreach (Equipment equipment in listEquipment.SelectedItems.OfType<Equipment>()) {
cmd.Parameters["#TypeID"].Value = equipment.equipmentID;
cmd.ExecuteNonQuery();
}
}
I seem to be having problems with my method that will return an Integer. I am attempting to modify the rows of a particular column with this returning Integer. The database will update the pre-existing column values with this new returned value. However, it appears that every row is being modified to the LAST row's value, regardless of what the specific row held previously. I am sure my code is just overwriting the variable, but I am wondering where. Here is my method; would appreciate feedback.
private int extractValue()
{
if (connection.State != ConnectionState.Open)
{
this.connection.Open();
}
ParsingHelper helper = null // different class - no issues with this.
String query = "SELECT device FROM dLogger";
OdbcCommand command = new OdbcCommand(query, this.connection);
List<Int32> list = new List<Int32>();
OdbcDataReader reader = null;
reader = command.ExecuteReader();
while (reader.Read())
{
list.Add(reader.GetInt32(0));
for (int i = 0; i < reader.FieldCount; i++)
{
helper = new ParsingHelper();
helper.assemble(list[i]);
}
}
return helper.getFirst();
}
No problem with the ParsingHelper here, it does the correct work. My problem is the overwriting. I thought a List would alleviate this issue but I am missing something, evidently.
EDIT: Would this approach work better?
while(reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
list.Add(reader.GetInt32(i));
//....
}
If my table originally looked like this:
ColA
1
2
3
4
And my function, for example, multiplied each number by 2. The new column would look like
ColA
8 // rather than 2
8 // rather than 4
8 // rather than 6
8 // 8 is the last value - therefore, correct.
So you see, I am running into some overwriting issues here. It appears the reader will read effectively and to the last row but it is not modifying values correctly, it is only assigning each value to the last value.
EDIT:
Here is where I am updating my database:
private void update()
{
String query = "UPDATE dLogger SET device = ?";
OdbcCommand command = new OdbcCommand(query, this.connection);
if (this.connection.State != ConnectionState.Open)
{
this.connection.Open();
}
command.Parameters.AddWithValue("?", extractValue());
}
Also, here is my simple Parsing Helper Class assemble()
private void assemble(int value)
{
setFirst(value);
}
private void setFirst(int value)
{
value = value * 2;
}
Just change your
String query = "SELECT device FROM dLogger";
to
String query = "UPDATE dLogger SET device=device*2";
thus:
private void extractValue()
{
if (connection.State != ConnectionState.Open)
{
this.connection.Open();
}
String query = "UPDATE dLogger SET device=device*2";
OdbcCommand command = new OdbcCommand(query, this.connection);
command.Execute();
}
There are so many questions on SO on this exception. But none of them are useful to me.
Here is my Stored Procedure :
CREATE PROCEDURE HolidayStandardMappingInsert
#HolidayID bigint,
#StandatdID smallint
AS
BEGIN
INSERT INTO HolidayStandardMapping VALUES(#HolidayID,#StandatdID)
END
GO
And here is my Code:
int StdId = 0;
SqlCommand cmdS = new SqlCommand("HolidayStandardMappingInsert", conn);
cmdS.CommandType = CommandType.StoredProcedure;
for (int i = 0; i < cblStandard.Items.Count; i++)
{
if (cblStandard.Items[i].Selected == true)
{
if (StdId == 0)
StdId = Convert.ToInt32(cblStandard.Items[i].Value);
else
StdId = Convert.ToInt32(cblStandard.Items[i].Value);
cmdS.Parameters.AddWithValue("#HolidayID", NewRecordID);
cmdS.Parameters.AddWithValue("#StandatdID", StdId);
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn.Open();
int res = cmdS.ExecuteNonQuery();
if (res > 0)
{
}
}
}
Tell me what is missing?
You are using same SqlCommnad object for multiple insertions so previously added parameters are also present.
So either create a new SqlCommnad object inside loop or clear prevoius parameters.
This is how you can Clear Previously added parameters.
cmdS.Parameters.Clear();
You are adding parameters in a loop.
So after second iteration, your command has 4 parameters.
Each time You are adding cmdS.Parameters.AddWithValue in a loop. So after the first iteration, it has already 2 parameters.
You need to clear the command parameters by cmdS.Parameters.Clear() before entering the loop.
I am using c# and sqlite, I want to be able to pass an array of bound parameters to a function so that I can insert or update via the function.
Essentially I want to do what I would normally do in PHP with PDO and bind the parameters using ? and passing an array when I execute the statement and it would attach them in order. But I don't know how to do something similar in C# and sqlite.
(I realize my set up may have mistakes or be inefficient or even have general coding errors. Any examples of how to set this up correctly, especially a full working example would be greatly appreciated.)
What I want to do would look something like this:
List<string> sqlParameters = new List<string>();
sqlParameters.Add("Red");
sqlParameters.Add("Blue");
sqlParameters.Add("Green");
updateDatabase("update table set column1 = #column1, column2= #column2 where column3 = #column3", sqlParameters);
int updateDatabase(string sql, List<string> sqlParameters)
{
try
{
dbConnection = new SQLiteConnection("Data Source=database.sqlite;Version=3;FailIfMissing=True");
dbConnection.Open();
sqlcommand.Prepare();
// I want to switch this next line somehow with something that allows me to simply
// pass the bound parameters list or array all at once and it attaches them in
// order. Similar to PDO using ? and passing an array in PHP.
sqlcommand.Parameters.AddWithValue("#column1", sqlParameters);
SQLiteCommand command = new SQLiteCommand(sql, dbConnection);
int affectedRows = command.ExecuteNonQuery();
dbConnection.Close();
return affectedRows;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
return 0;
}
---------------EDIT - result after help from PookyFan:---------------
In case anyone cares to use this, my final function ended up looking like this:
List<string> sqlParameters = new List<string>();
sqlParameters.Add("red");
sqlParameters.Add("blue");
updateDatabase("insert or replace into chattracking (column1, column2) VALUES (#param0, #param1)",sqlParameters);
int updateDatabase(string sql, List<string> sqlParameters)
{
try {
dbConnection =
new SQLiteConnection("Data Source=databasename.sqlite;Version=3;FailIfMissing=True");
dbConnection.Open();
SQLiteCommand command = new SQLiteCommand(sql, dbConnection);
command.Prepare();
for (int i = 0; i < sqlParameters.Count; i++) {
command.Parameters.AddWithValue(("#param" + i.ToString()), sqlParameters[i]);
}
int affectedRows = command.ExecuteNonQuery();
dbConnection.Close();
return affectedRows;
} catch (Exception e) {
MessageBox.Show(e.ToString());
}
return 0;
}
Something like this?
for(int i = 0; i < sqlParameters.Count; ++i)
sqlcommand.Parameters.AddWithValue("#column" + i.ToString(), sqlParameters[i]);