Multi-statement SQL transaction failing at null parameter - c#

I have a multi-statement SQL query that takes place within a SqlTransaction as follows:
string sName = "";
string sNumber = "";
string sFirstName = "";
string sqlQuery1 = #"INSERT INTO myTable(Name, Number) VALUES (#_Name, #_Number)";
string sqlQuery2 = #"INSERT INTO myOtherTable( ID, FirstName) VALUES ( #_ID, #_First )";
SqlConnection conn = new SqlConn(***);
conn.Open();
SqlTransaction transaction;
SqlCommand command1 = new SqlCommand(sqlQuery1, conn, transaction);
SqlCommand command2 = new SqlCommand(sqlQuery2, conn, transaction);
command1.Parameters.Add("#_Name", SqlDbType.NVarChar, 255).Value = sName;
command1.Parameters.Add("#_Number", SqlDbType.NVarChar, 255).Value = sNumber;
int? returnedID = (int?)command1.ExecuteScalar();
command2.Parameters.Add("#_ID", SqlDbType.Int).Value = (int)returnedID; <--- Error
command2.Parameters.Add("#_First", SqlDbType.NVarChar, 255).Value = sFirstName;
command2.ExecuteNonQuery();
transaction.Commit();
At the line I marked as an error, I get an error during execution about "The parameterized query expects the parameter #_ID which was not supplied.
Now, assuming I have no typos while simplifying my code to this example, why am I receiving a null value in returnedID for a properly executed statement? When I just run the statement on its own in a query, I get no errors and it returns successful. Why null when running it in this transaction? Thanks!
Edit: I purposely left off a try{} catch from this as it wasn't relevant to the question I had. Otherwise, I just forgot about the IDENTITY_SCOPE() that I needed in the Insert query since there would otherwise be no return value.

ExecuteScalar returns the first column of the first row in the result set returned by the query (MSDN). Your statement is an insert, it does not return any result set.
The easiest way to fix your example is to run all queries in one command and use SCOPE_IDENTITY to get the inserted id.
string sqlQuery =
#"INSERT INTO myTable(Name, Number) VALUES (#_Name, #_Number);
INSERT INTO myOtherTable( ID, FirstName) VALUES ( SCOPE_IDENTITY(), #_First )";

Since insert statements don't return a value, ExecuteScalar() can't return the identity as you want. You can combine the insert with a select scope_identity() as in the example on the ExecuteScalar() documentation to get the functionality I think you're expecting.

You seem to assume that INSERT INTO myTable(Name, Number) VALUES (#_Name, #_Number) is going to return the ID of the inserted row; why would you think that? The assumption is incorrect, which is why you're getting a null value back from ExecuteScalar.
ExecuteScalar returns null if the command's result set is empty, and the result set of an INSERT statement is in fact empty, unless you include an OUTPUT clause.

Related

SQL query result to C# Variable

I'm trying to pull data from an SQL variable in C# to use in another SQL query.
Basically I have a for loop that is running through a datagrid and inserting the data into a table which I need to be linked to #DataID in this query below. As it is in a different query I can't access it so I want to pull it out into a var.
What's the best way to go about this? already searched lots of options and not coming up with anything that works
The help is appreciated!
Cheers
string dartBoxQuery = #"DECLARE #DataID int;
INSERT INTO DartBox (DartBoxNumber, ReturnDate, Comments)
VALUES (#dbn, #rtndate, #cmmts)
SELECT #DataID = scope_identity();";
// set up the command before exec
SqlCommand cmd = new SqlCommand(dartBoxQuery, con);
//set parameters
cmd.Parameters.AddWithValue("#rtndate", dateTimePicker1.Text);
cmd.Parameters.AddWithValue("#dbn", textBox1.Text);
cmd.Parameters.AddWithValue("#cmmts", textBox2.Text);
// call SQL connection
con.Open();
// execute above query
cmd.ExecuteNonQuery();
//close connection
con.Close();
If you want to fetch #DataID back to the caller, there are 3 options:
declare an output parameter... presumably just moving #DataID to be an output parameter rather than a local variable; add an extra parameter and give it the direction of ParameterDirection.Output; after the ExecuteNonQuery, read out the value
at the end of your existing SQL, return #DataID; add an extra parameter and give it the direction of ParameterDirection.ReturnValue; after the ExecuteNonQuery, read out the value
at the end of your existing SQL, select #DataID; use ExecuteScalar and read out the return value
In this case, ExecuteScalar is probably the easiest option:
string dartBoxQuery = #"DECLARE #DataID int;
INSERT INTO DartBox (DartBoxNumber, ReturnDate, Comments)
VALUES (#dbn, #rtndate, #cmmts)
SELECT #DataID = scope_identity();
SELECT #DataID";
// set up the command before exec
SqlCommand cmd = new SqlCommand(dartBoxQuery, con);
//set parameters
cmd.Parameters.AddWithValue("#rtndate", dateTimePicker1.Text);
cmd.Parameters.AddWithValue("#dbn", textBox1.Text);
cmd.Parameters.AddWithValue("#cmmts", textBox2.Text);
// call SQL connection
con.Open();
// execute above query
var dataId = Convert.ToInt32(cmd.ExecuteScalar());
//close connection
con.Close();

How to get string value from an OracleParameter object when using ExecuteNonQuery

As the code Shows below, I want to insert a row into a database table (oracle 11) and return a String-Value of the inserted row.
using (OracleCommand cmd = con.CreateCommand()) {
cmd.CommandText = "insert into foo values('foo','bar') returning idString into :lastIdParam";
cmd.Parameters.Add(new OracleParameter("lastIdParam", OracleDbType.Varchar2), ParameterDirection.ReturnValue);
cmd.ExecuteNonQuery(); // returning 1 (insert row successfully)
var result = cmd.Parameters["lastIdParam"].Value.ToString(); // == String.Empty
}
Debugging shows that lastIdParam.Value's value = Empty.String:
My Problem is, that I'm not getting the return string into my return-parameter but it will work when returning an integer value (like sequence no of inserted id). Cast Problem? ...?
The idString is filled if running the Statement directly (or if I just do something like returning 'ABC' into :myOutputParameter
Any ideas how to retrieve a string after inserting row?
Have you tried setting a size for the parameter? The default size is 0.
new OracleParameter("lastIdParam", OracleDbType.Varchar2, 128)
The idString is an expression which has no value in your context, unless it is a column name in your table. Therefore, it is epected to be empty. You may change your query like the example below and see what happens.
cmd.CommandText = "insert into foo values('foo','bar') returning hereYouHaveToUseAColumnFromTheFooTable into :lastIdParam";

What is the wrong with Scope_Identity?

I tried milions of methods to make scope identity work. It is just returns __Page !!
Query = "INSERT INTO seekers(name,sname,lname,status,gender,dob,major,experince,email,password,phone,valid,city) values(#name,#sname,#lname,#status,#gender,#dob,#major,#exp,#email,#password,#phone,0,#city);SELECT SCOPE_IDENTITY();";
// setting up command definition
Command = new SqlCommand(Query, Connection);
// setting up command parameters
Command.Parameters.AddWithValue("email", txt_email.Text);
Command.Parameters.AddWithValue("gender", lst_gender.SelectedValue);
Command.Parameters.AddWithValue("status", lst_status.SelectedValue);
Command.Parameters.AddWithValue("phone", long.Parse("968" + txt_phone.Text));
Command.Parameters.AddWithValue("password", txt_password.Text);
Command.Parameters.AddWithValue("exp", lst_exp.SelectedValue);
Command.Parameters.AddWithValue("city", lst_exp.SelectedValue);
Command.Parameters.AddWithValue("major", lst_major.SelectedValue);
Command.Parameters.AddWithValue("name", txt_name.Text);
Command.Parameters.AddWithValue("sname", txt_sname.Text);
Command.Parameters.AddWithValue("lname", txt_lname.Text);
Command.Parameters.AddWithValue("dob", cld_birth.SelectedDate);
int ID = (int)Command.ExecuteScalar();
Try Out Parameter as follow...
Query = "INSERT INTO seekers(name,sname,lname,status,gender,dob,major,experince,email,password,phone,valid,city) values(#name,#sname,#lname,#status,#gender,#dob,#major,#exp,#email,#password,#phone,0,#city);SET #ID=SCOPE_IDENTITY();"
//Your Parameters..
SqlParameter ID=new SqlParameter();
ID.Name="#ID";
ID.Direction=ParameterDirection.Output;
Command.Parameters.Add(ID);
Command.ExecuteNonQuery();
int id=(int)ID.Value;
or
Tryb to Cast Output as follow...
Query = "INSERT INTO seekers(name,sname,lname,status,gender,dob,major,experince,email,password,phone,valid,city) values(#name,#sname,#lname,#status,#gender,#dob,#major,#exp,#email,#password,#phone,0,#city);SELECT CAST(scope_identity() AS int);"
int id= (Int32)Command.ExecuteScalar();
use set nocount off and try
As First execute statement is insert the returned value is number of records effected
SCOPE_IDENTITY will return a decimal, try:
int ID = (int) (decimal) Command.ExecuteScalar();
It's not very clear what you mean by:
It is just returns __Page !!
Presumably the posted code is throwing an exception, and that's resulting in some higher level code doing whatever it is you mean by "... returns __Page".
If you look at the exception details, you'll find more about what happened: which I suspect is an InvalidCastException because you're trying to cast the object returned by Command.ExecuteScalar (a boxed decimal) to an int.

Unable to insert data into SQL Database using C#

I'm writing a method to insert a Student into a local SQL database that contains a table with information about Students:
public void AddStudent(string name, string teachName, string pass)
{
string dbfile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\Logo.sdf";
SqlCeConnection connection = new SqlCeConnection("Data Source=" + dbfile + "; Password = 'dbpass2011!'");
connection.Open();
SqlCeTransaction transaction = connection.BeginTransaction();
SqlCeCommand command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "INSERT INTO Students VALUES ('#name', '#id', '#pass', '#tname')";
command.Parameters.Add("#name", name);
command.Parameters.Add("#id", this.ID);
command.Parameters.Add("#pass", MD5Encrypt.MD5(pass));
command.Parameters.Add("#tname", teachName);
command.Prepare();
command.ExecuteNonQuery();
transaction.Commit();
connection.Dispose();
connection.Close();
}
Whenever I use this, it never inserts the data to the table when I look at the contents of the Students table in the database. Originally I had this return an int so I could see how many rows it affected, which it always returned 1, so I know it's working.
I've looked for answers to this, and the answer to similar questions was that the person asking was looking at the wrong .sdf file. I've made sure that I'm looking at the right file.
Any feedback would be much appreciated!
command.CommandText = "INSERT INTO Students VALUES ('#name', '#id', '#pass', '#tname')";
You should remove the extra single quotes - this should be:
command.CommandText = "INSERT INTO Students VALUES (#name, #id, #pass, #tname)";
Also I am not sure why you open a transaction for a single insert - that is also not needed.
You don't need to put single quote to parametrized query, in case of parametrized query the whole data will be parsed as required,
command.CommandText = "INSERT INTO Students VALUES (#name, #id, #pass, #tname)";
Also, its better to set parameter type, size and value explicitly as below:
SqlCeParameter param = new SqlCeParameter("#name", SqlDbType.NVarChar, 100);
param.Value = name; // name is a variable that contain the data of name field
//param.Value = 'Jhon Smith'; //Directly value also can be used
Hope this would be helpful, thanks for your time.
There is most likely an exception being raised in your code; you need to add a try/catch handler and/or debug the application to figure out exactly what is happening.
However, there are at least two issues with your code:
The prepare statement requires the data types of the parameters. From the MSDN documentation:
Before you call Prepare, specify the data type of each parameter in the statement to be prepared. For each parameter that has a variable-length data type, you must set the Size property to the maximum size needed. Prepare returns an error if these conditions are not met.
You need to close the connection before disposing it (this won't affect the insert, however).

Specific cast is not valid, while retrieving scope_identity

I am getting exception: "Specific cast is not valid", here is the code
con.Open();
string insertQuery = #"Insert into Tender (Name, Name1, Name2) values ('Val1','Val2','Val3');Select Scope_Identity();";
SqlCommand cmd = new SqlCommand(insertQuery, con);
cmd.ExecuteNonQuery();
tenderId = (int)cmd.ExecuteScalar();
In the interests of completeness, there are three issues with your code sample.
1) You are executing your query twice by calling ExecuteNonQuery and ExecuteScalar. As a result, you will be inserting two records into your table each time this function runs. Your SQL, while being two distinct statements, will run together and therefore you only need the call to ExecuteScalar.
2) Scope_Identity() returns a decimal. You can either use Convert.ToInt32 on the result of your query, or you can cast the return value to decimal and then to int.
3) Be sure to wrap your connection and command objects in using statements so they are properly disposed.
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(sql, connection))
{
connection.Open();
int tenderId = (int)(decimal)command.ExecuteScalar();
}
}
Try this:-
con.Open();
string insertQuery = #"Insert into Tender (Name, Name1, Name2) values ('Val1','Val2','Val3');Select Scope_Identity();";
SqlCommand cmd = new SqlCommand(insertQuery, con);
tenderId = Convert.ToInt32(cmd.ExecuteScalar());
EDIT
It should be this as it is correctly pointed out that scope_identity() returns a numeric(38,0) :-
tenderId = Convert.ToInt32(cmd.ExecuteScalar());
Note: You still need to remove the:-
cmd.ExecuteNonQuery();
Test the following first:
object id = cmd.ExcuteScalar()
Set a break point and have a look at the type of id. It is probably a Decimal and cannot directly be casted to int.
it needs Convert.ToInt32(cmd.ExecuteScalar());

Categories

Resources