SQL query with c# stringbuilder error near "=" symbol - c#

I'm working on some code that creates procedures with C#. When I try to run my code:
StringBuilder sbSP1 = new StringBuilder();
sbSP1.AppendLine("CREATE PROCEDURE " + ProcN + "DELETE #id int=0 AS BEGIN DELETE FROM " + tname + " WHERE " + PK + " = #id END");
SqlConnection connection = new SqlConnection(connectionString);
using (SqlCommand cmd = new SqlCommand(sbSP1.ToString(), connection))
{
connection.Open();
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
connection.Close();
}
This part doesn't work. It throws an exception:
System.Data.SqlClient.SqlException: 'Incorrect syntax near '='.'
But when I try to run this part:
StringBuilder sbSP3 = new StringBuilder();
sbSP3.AppendLine("CREATE PROCEDURE " + ProcN + "GET AS BEGIN SET NOCOUNT ON; SELECT " + columns + " from " + tname + " END");
SqlConnection connection = new SqlConnection(connectionString);
using (SqlCommand cmd = new SqlCommand(sbSP3.ToString(), connection))
{
connection.Open();
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
connection.Close();
}
it runs correctly.
Where is my mistake in the first snippet of code?

Without sbSP1 final text it's difficult to pin down the error; as a working hypothesis I suggest that PK being complex (e.g. "id value")
should be escaped - [PK]. In order to avoid such kind of errors, please, never build the query, but make procedure's text being readable with a help of string interpolation - $ and verbatim strings - #.
//TODO: check the text, is it the procedure you want to create?
string text =
$#"create procedure [{ProcN}Delete]
#id int = 0 as
begin
delete
from [{tname}]
where [{PK}] = #id
end;";
//DONE: do not close connection manually, but wrap it into using
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
using (qlCommand cmd = new SqlCommand(text, connection)) {
cmd.ExecuteNonQuery();
}
}

Related

Invalid attempt to call read when reader is closed when inserting data

i have a button that when clicked inserts data from textbox and combobox fields into database tables, but every time i insert it gives me "Invalid attempt to call read when reader is closed". How can i get rid of this error. And tips on optimising the code are welcome, because i know im a total noob. thanks
private void btnSave_Click(object sender, RoutedEventArgs e)
{
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query1 = "insert into location(Storage, Shelf, columns, rows) values(" + txtWarehouse.Text + ", " + txtShelf.Text + ", " + txtColumn.Text + ", " + txtRow.Text + ")";
SqlCommand sqlCmd = new SqlCommand(Query1, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
sqlCon.Close();
}
catch (Exception er)
{
MessageBox.Show(er.Message);
}
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query3 = "SELECT LOCATION_ID FROM LOCATION WHERE storage='" + txtWarehouse.Text + "' AND shelf='" + txtShelf.Text + "' AND columns='"
+ txtColumn.Text + "' AND rows='" + txtRow.Text + "'";
SqlCommand sqlCmd1 = new SqlCommand(Query3, sqlCon);
SqlDataReader dr = sqlCmd1.ExecuteReader(); ;
while (dr.Read())
{
string LocationId = dr[0].ToString();
dr.Close();
string Query2 = "insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID) values ('" + txtSku.Text + "','" + txtNimetus.Text + "', '"
+ txtMin.Text + "', '" + txtMax.Text + "', '" + txtQuan.Text + "', '" + LocationId + "', '" + (cbCat.SelectedIndex+1) + "', '" + (cbMail.SelectedIndex+1) + "')";
SqlCommand sqlCmd = new SqlCommand(Query2, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
}
sqlCon.Close();
}
catch (Exception ed)
{
MessageBox.Show(ed.Message);
}
}
Let's try to make some adjustments to your code.
First thing to consider is to use a parameterized query and not a
string concatenation when you build an sql command. This is mandatory
to avoid parsing errors and Sql Injections
Second, you should encapsulate the disposable objects in a using statement
to be sure they receive the proper disposal when you have finished to
use them.
Third, you can get the LOCATION_ID from your table without running a
separate query simply adding SELECT SCOPE_IDENTITY() as second batch to your first command. (This works only if you have declared the LOCATION_ID field in the first table as an IDENTITY column)
Fourth, you put everything in a transaction to avoid problems in case
some of the code fails unexpectedly
So:
SqlTransaction tr = null;
try
{
string cmdText = #"insert into location(Storage, Shelf, columns, rows)
values(#storage,#shelf,#columns,#rows);
select scope_identity()";
using(SqlConnection sqlCon = new SqlConnection(.....))
using(SqlCommand cmd = new SqlCommand(cmdText, sqlCon))
{
sqlCon.Open();
using( tr = sqlCon.BeginTransaction())
{
// Prepare all the parameters required by the command
cmd.Parameters.Add("#storage", SqlDbType.Int).Value = Convert.ToInt32(txtWarehouse.Text);
cmd.Parameters.Add("#shelf", SqlDbType.Int).Value = Convert.ToInt32(txtShelf.Text);
cmd.Parameters.Add("#columns", SqlDbType.Int).Value = Convert.ToInt32(txtColumn.Text );
cmd.Parameters.Add("#rows", SqlDbType.Int).Value = Convert.ToInt32(txtRow.Text);
// Execute the command and get back the result of SCOPE_IDENTITY
int newLocation = Convert.ToInt32(cmd.ExecuteScalar());
// Set the second command text
cmdText = #"insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID)
values (#sku, #nimetus,#min,#max,#qty,#locid,#catid,#ordid)";
// Build a new command with the second text
using(SqlCommand cmd1 = new SqlCommand(cmdText, sqlCon))
{
// Inform the new command we are inside a transaction
cmd1.Transaction = tr;
// Add all the required parameters for the second command
cmd1.Parameters.Add("#sku", SqlDbType.NVarChar).Value = txtSku.Text;
cmd1.Parameters.Add("#nimetus",SqlDbType.NVarChar).Value = txtNimetus.Text;
cmd1.Parameters.Add("#locid", SqlDbType.Int).Value = newLocation;
.... and so on for the other parameters required
cmd1.ExecuteNonQuery();
// If we reach this point the everything is allright and
// we can commit the two inserts together
tr.Commit();
}
}
}
}
catch (Exception er)
{
// In case of exceptions do not insert anything...
if(tr != null)
tr.Rollback();
MessageBox.Show(er.Message);
}
Notice that in the first command I use parameters of type SqlDbType.Int because you haven't used single quotes around your text. This should be verified against the real data type of your table columns and adjusted to match the type. This is true as well for the second command where you put everything as text albeit some of those fields seems to be integer (_location_id_ is probably an integer). Please verify against your table.

multiple queries on 1 button click

I want to perform 2 queries in one button click. I tried the
string query = "first query";
query+="second query";
But this didn't work it shows error.
I have now created 2 separate connections like below:
try
{
SqlConnection conn1 = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionStringDatabase"].ConnectionString);
//open connection with database
conn1.Open();
//query to select all users with teh given username
SqlCommand com1 = new SqlCommand("insert into artikulli (tema,abstrakti, kategoria_id, keywords ) values (#tema, #abstrakti, #kategoria, #keywords)", conn1);
// comand.Parameters.AddWithValue("#id", iD);
com1.Parameters.AddWithValue("#tema", InputTitle.Value);
com1.Parameters.AddWithValue("#abstrakti", TextareaAbstract.Value);
com1.Parameters.AddWithValue("#kategoria", DropdownCategory.Value);
com1.Parameters.AddWithValue("#keywords", InputTags.Value);
//execute queries
com1.ExecuteNonQuery();
conn1.Close();
if (FileUploadArtikull.HasFile)
{
int filesize = FileUploadArtikull.PostedFile.ContentLength;
if (filesize > 4194304)
{
ScriptManager.RegisterStartupScript(this, this.GetType(), "popup", "alert('Maximumi i madhesise eshte 4MB');", true);
}
else
{
string filename = "artikuj/" + Path.GetFileName(FileUploadArtikull.PostedFile.FileName);
SqlConnection conn2 = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionStringDatabase"].ConnectionString);
SqlCommand com2 = new SqlCommand("insert into artikulli(path) values ('" + filename + "')", conn2);
//open connection with database
conn2.Open();
com2.ExecuteNonQuery();
FileUploadArtikull.SaveAs(Server.MapPath("~/artikuj\\" + FileUploadArtikull.FileName));
Response.Redirect("dashboard.aspx");
}
}
else
{
ScriptManager.RegisterStartupScript(this, this.GetType(), "popup", "alert('Ju nuk keni perzgjedhur asnje file');", true);
}
}
But the problem is that only the second query is performed and the firs is saved as null in database
In your case, there is no reason to open two connections. In addition, the C# language has evolved, so I recommend using the power given by the new language constructs (using, var).
Here is an improved version that should work assuming that the values you bind to your parameters are valid:
try
{
using(var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionStringDatabase"].ConnectionString))
{
//open connection with database
connection.Open();
//query to select all users with teh given username
using(var command1 = new SqlCommand("insert into artikulli (tema,abstrakti, kategoria_id, keywords ) values (#tema, #abstrakti, #kategoria, #keywords)", connection))
{
command1.Parameters.AddWithValue("#tema", InputTitle.Value);
command1.Parameters.AddWithValue("#abstrakti", TextareaAbstract.Value);
command1.Parameters.AddWithValue("#kategoria", DropdownCategory.Value);
command1.Parameters.AddWithValue("#keywords", InputTags.Value);
//execute first query
command1.ExecuteNonQuery();
}
//build second query
string filename = "artikuj/" + Path.GetFileName(FileUploadArtikull.PostedFile.FileName);
using(SqlCommand command2 = new SqlCommand("insert into artikulli(path) values (#filename)", connection))
{
//add parameters
command2.Parameters.AddWithValue("#filename", filename);
//execute second query
command2.ExecuteNonQuery();
}
}
}
//TODO: add some exception handling
//simply wrapping code in a try block has no effect without a catch/finally
Try below code, No need to open the connection twice
string query1 = "insert into artikulli (tema,abstrakti, kategoria_id, keywords ) values (#tema, #abstrakti, #kategoria, #keywords)";
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionStringDatabase"].ConnectionString);
SqlCommand com1= new SqlCommand(query1, conn);
com1.Parameters.AddWithValue("#tema", InputTitle.Value);
com1.Parameters.AddWithValue("#abstrakti", TextareaAbstract.Value);
com1.Parameters.AddWithValue("#kategoria", DropdownCategory.Value);
com1.Parameters.AddWithValue("#keywords", InputTags.Value);
string query2 = "insert into artikulli(path) values ('" + filename + "')", conn);
comm.ExecuteNonQuery();
comm.CommandText = query2;
comm.ExecuteScalar();

How to run multiple SQL commands in a single SQL connection?

I am creating a project in which I need to run 2-3 SQL commands in a single SQL connection.
Here is the code I have written:
SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\project.mdf;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand("select * from " + mytags.Text + " ", con);
SqlDataReader rd = cmd.ExecuteReader();
if (rd.Read())
{
con.Close();
con.Open();
SqlCommand cmd1 = new SqlCommand("insert into " + mytags.Text + " values ('fname.lname#gmail.com','" + TextBox3.Text + "','" + TextBox4.Text + "','" + TextBox5.Text + "','"+mytags.Text+"')", con);
cmd1.ExecuteNonQuery();
label.Visible = true;
label.Text = "Date read and inserted";
}
else
{
con.Close();
con.Open();
SqlCommand cmd2 = new SqlCommand("create table " + mytags.Text + " ( session VARCHAR(MAX) , Price int , Description VARCHAR(MAX), Date VARCHAR(20),tag VARCHAR(10))", con);
cmd2.ExecuteNonQuery();
con.Close();
con.Open();
SqlCommand cmd3 = new SqlCommand("insert into " + mytags.Text + " values ('" + Session + "','" + TextBox3.Text + "','" + TextBox4.Text + "','" + TextBox5.Text + "','" + mytags.Text + "')", con);
cmd3.ExecuteNonQuery();
label.Visible = true;
label.Text = "tabel created";
con.Close();
}
I have tried to remove the error and I got that the connection is not going to else condition. Please review the code and suggest if there is any mistake or any other solution for this.
Just change the SqlCommand.CommandText instead of creating a new SqlCommand every time. There is no need to close and reopen the connection.
// Create the first command and execute
var command = new SqlCommand("<SQL Command>", myConnection);
var reader = command.ExecuteReader();
// Change the SQL Command and execute
command.CommandText = "<New SQL Command>";
command.ExecuteNonQuery();
The following should work. Keep single connection open all time, and just create new commands and execute them.
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command1 = new SqlCommand(commandText1, connection))
{
}
using (SqlCommand command2 = new SqlCommand(commandText2, connection))
{
}
// etc
}
Just enable this property in your connection string:
sqb.MultipleActiveResultSets = true;
This property allows one open connection for multiple datareaders.
I have not tested , but what the main idea is: put semicolon on each query.
SqlConnection connection = new SqlConnection();
SqlCommand command = new SqlCommand();
connection.ConnectionString = connectionString; // put your connection string
command.CommandText = #"
update table
set somecol = somevalue;
insert into someTable values(1,'test');";
command.CommandType = CommandType.Text;
command.Connection = connection;
try
{
connection.Open();
}
finally
{
command.Dispose();
connection.Dispose();
}
Update:
you can follow
Is it possible to have multiple SQL instructions in a ADO.NET Command.CommandText property? too
This is likely to be attacked via SQL injection by the way. It'd be worth while reading up on that and adjusting your queries accordingly.
Maybe look at even creating a stored proc for this and using something like sp_executesql which can provide some protection against this when dynamic sql is a requirement (ie. unknown table names etc). For more info, check out this link.
No one has mentioned this, but you can also separate your commands using a ; semicolon in the same CommandText:
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand comm = new SqlCommand())
{
comm.Connection = conn;
comm.CommandText = #"update table ... where myparam=#myparam1 ; " +
"update table ... where myparam=#myparam2 ";
comm.Parameters.AddWithValue("#myparam1", myparam1);
comm.Parameters.AddWithValue("#myparam2", myparam2);
conn.Open();
comm.ExecuteNonQuery();
}
}
Multiple Non-query example if anyone is interested.
using (OdbcConnection DbConnection = new OdbcConnection("ConnectionString"))
{
DbConnection.Open();
using (OdbcCommand DbCommand = DbConnection.CreateCommand())
{
DbCommand.CommandText = "INSERT...";
DbCommand.Parameters.Add("#Name", OdbcType.Text, 20).Value = "name";
DbCommand.ExecuteNonQuery();
DbCommand.Parameters.Clear();
DbCommand.Parameters.Add("#Name", OdbcType.Text, 20).Value = "name2";
DbCommand.ExecuteNonQuery();
}
}
Here you can find Postgre example, this code run multiple sql commands (update 2 columns) within single SQL connection
public static class SQLTest
{
public static void NpgsqlCommand()
{
using (NpgsqlConnection connection = new NpgsqlConnection("Server = ; Port = ; User Id = ; " + "Password = ; Database = ;"))
{
NpgsqlCommand command1 = new NpgsqlCommand("update xy set xw = 'a' WHERE aa='bb'", connection);
NpgsqlCommand command2 = new NpgsqlCommand("update xy set xw = 'b' where bb = 'cc'", connection);
command1.Connection.Open();
command1.ExecuteNonQuery();
command2.ExecuteNonQuery();
command2.Connection.Close();
}
}
}
using (var connection = new SqlConnection("Enter Your Connection String"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "Enter the First Command Here";
command.ExecuteNonQuery();
command.CommandText = "Enter Second Comand Here";
command.ExecuteNonQuery();
//Similarly You can Add Multiple
}
}
It worked for me.

Stored Procedures with Mysql connector

I'm trying to call a simple stored procedure in c# 2010.
With only a IN argument it's ok, but now with a OUT argument it's not working.
In phpmyadmin :
drop procedure if exists insert_artist;
delimiter $$
create procedure insert_student(IN name VARCHAR(100), OUT id INT)
begin
insert into student(name) values(name);
set id = last_insert_id();
end$$
delimiter ;
Then using
call insert_student("toto",#id);
select #id;
It's working fine.
Now, in c# :
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
using (MySqlCommand command = connection.CreateCommand())
{
command.CommandText = "insert_student";
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("#name", "xxxx");
command.Parameters.AddWithValue("#id",MySqlDbType.Int32);
command.ExecuteNonQuery();
Console.WriteLine("**** " + command.Parameters["#id"].Value);
}
}
Gives me an exception when executing ExecuteNonQuery() :
OUT or INOUT argument 2 for routine insert_student is not a variable or NEW pseudo-variable in BEFORE trigger
The same thing without the out argument in the stored procedure is working fine.
Where is my mistake?
A fuller example:
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand(nameOfStoredRoutine, connection);
cmd.CommandType = CommandType.StoredProcedure;
//input parameters
for (int i = 0; i < (parameterValue.Length / 2); i++)
{
cmd.Parameters.AddWithValue(parameterValue[i, 0], parameterValue[i, 1]);
cmd.Parameters[parameterValue[i, 0]].Direction = ParameterDirection.Input;
parameterList = parameterList + parameterValue[i,0] + " " + parameterValue[i,1] + " ";
}
//single output parameter
cmd.Parameters.AddWithValue("#output", MySqlDbType.Int32);
cmd.Parameters["#output"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery(); //Execute command
this.CloseConnection(); //close connection
return Convert.ToInt32(cmd.Parameters["#output"].Value.ToString());
my below code works
pls check if it's ok for you.
InsertQuery = New MySqlCommand("xxxxxx")
InsertQuery.Connection = Connection
InsertQuery.CommandType = Data.CommandType.StoredProcedure
InsertQuery.Parameters.AddWithValue("IN_xxx", str_xxxx)
InsertQuery.Parameters.Add("OUT_LastID", MySqlDbType.Int32).Direction = ParameterDirection.Output
IQ = InsertQuery.ExecuteReader()
IQ.Read()
LASTID = InsertQuery.Parameters("OUT_LastID").Value

SCOPE_IDENTITY is not working in asp.net?

I am trying to insert a record and get its newly generated id by executing two queries one by one, but don't know why its giving me the following error.
Object cannot be cast from DBNull to other types
My code is as below: (I don't want to use sql stored procedures)
SqlParameter sqlParam;
int lastInsertedVideoId = 0;
using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString))
{
Conn.Open();
using (SqlCommand sqlCmd = Conn.CreateCommand())
{
string sqlInsertValues = "#Name,#Slug";
string sqlColumnNames = "[Name],[Slug]";
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");";
sqlCmd.CommandText = sqlQuery;
sqlCmd.CommandType = CommandType.Text;
sqlParam = sqlCmd.Parameters.Add("#Name", SqlDbType.VarChar);
sqlParam.Value = txtName.Text.Trim();
sqlParam = sqlCmd.Parameters.Add("#Slug", SqlDbType.VarChar);
sqlParam.Value = txtSlug.Text.Trim();
sqlCmd.ExecuteNonQuery();
//getting last inserted video id
sqlCmd.CommandText = "SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]";
using (SqlDataReader sqlDr = sqlCmd.ExecuteReader())
{
sqlDr.Read();
lastInsertedVideoId = Convert.ToInt32(sqlDr["lastInsertedVideoId"]);
}
}
}
//tags insertion into tag table
if (txtTags.Text.Trim().Length > 0 && lastInsertedVideoId > 0)
{
string sqlBulkTagInsert = "";
string[] tags = txtTags.Text.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
foreach (string tag in tags)
{
sqlBulkTagInsert += "INSERT INTO tags(VideoId, Tag) VALUES(" + lastInsertedVideoId + ", " + tag.Trim().ToLowerInvariant()+ "); ";
}
using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString))
{
Conn.Open();
using (SqlCommand sqlCmd = Conn.CreateCommand())
{
string sqlQuery = sqlBulkTagInsert;
sqlCmd.CommandText = sqlQuery;
sqlCmd.CommandType = CommandType.Text;
sqlCmd.ExecuteNonQuery();
}
}
}
And also if possible, please check is the above code coded well or we can optimize it more for improve performance?
Thanks
The call to SCOPE_IDENTITY() is not being treated as being in the same "scope" as the INSERT command that you're executing.
Essentially, what you need to do is change the line:
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");";
to:
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + "); SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]";
and then call
int lastVideoInsertedId = Convert.ToInt32(sqlCmd.ExecuteScalar());
instead of .ExecuteNonQuery and the code block following the "//getting last inserted video id" comment.
The SCOPE_IDENTITY() should be extracted from the first command (SELECT, RETURN or OUT) and passed into the next command. By that, I mean that the SELECT_IDENTITY() should be at the end of the first command. In SQL 2008 there is additional syntax for bring values back as part of the INSERT, which makes this simpler.
Or more efficiently: combine the commands into one to avoid round-trips.

Categories