I tried to do begin transaction on SQL Server, but it returns an error that I can't figure out what the real problem is. So here is some of my code I tried.
This is the error:
Code:
SqlConnection connection = new SqlConnection("Data Source=LOCALHOST\\SQLEXPRESS;Initial Catalog=tempdb;Integrated Security=SSPI;User ID = xxxx; Password=xxx;");
DateTime dt = dateTimePicker1.Value.Date;
dt = dt.AddDays(60);
string selectQuery = "BEGIN TRANSACTION UPDATE tester SET
test_ad=#dateTimePicker1, test_ud=#dt, test_pd=#dt WHERE
test_name=#textBox1;INSERT INTO records(testr_status, testr_name, testr_ad,
testr_ud, testr_pd, apte_name)VALUES(#testr_status, testr_name = #comboBox1,
testr_ad = #dateTimePicker1, testr_ud = #dt, testr_pd = #dt COMMIT";
connection.Open();
SqlCommand command = new SqlCommand(selectQuery, connection);
command.Parameters.AddWithValue("#dateTimePicker1",this.dateTimePicker1.Value.Date);
command.Parameters.AddWithValue("#textBox1", this.textBox1.Text);
command.Parameters.AddWithValue("#comboBox1",this.comboBox1.SelectedItem);
command.Parameters.AddWithValue("#testr_status",SqlDbType.VarChar);
command.Parameters.AddWithValue("#dt", dt);
int iResult = command.ExecuteNonQuery();
if (iResult > 0)
MessageBox.Show("Successfully saved ", "Error",MessageBoxButtons.OK, MessageBoxIcon.Information);
else
MessageBox.Show("Record not saved ", "Error",MessageBoxButtons.OK, MessageBoxIcon.Error);
command.ExecuteNonQuery();
connection.Dispose();
command.Dispose();
Try cleaning up a bit your query or paste it on SSMS and declare your parameters and you will figure out what is wrong.
In your case your INSERT statement has some errors.
This is not valid syntax VALUES (test_name = #combobox1) instead you only pass the parameter VALUES (#combobox1)
There are more columns in the INSERT statement than values specified in the VALUES clause, you are not providing a value for apte_name. In the c# code you will need to add that parameter too.
You are missing the closing parenthesis for the VALUES clause
You should end up with something like this (not tested)
string selectQuery =
#"BEGIN TRANSACTION
UPDATE tester SET
test_ad = #dateTimePicker1,
test_ud = #dt,
test_pd = #dt
WHERE test_name = #textBox1;
INSERT INTO records
(
testr_status,
testr_name,
testr_ad,
testr_ud,
testr_pd,
apte_name
)
VALUES
(
#testr_status,
#comboBox1,
#dateTimePicker1,
#dt,
#dt,
#apte_name
);
COMMIT";
The actual problem is, that is one big, invalid SQL statement. Use the semi-colon to separate statements, like so:
"BEGIN TRANSACTION;
INSERT ...;
UPDATE ...;
ETC ...;
COMMIT;"
That said, don't embed transaction statements in a query string. Do what Oliver suggests in another answer.
You can use SqlTransaction
using (SqlConnection conn = new SqlConnection("Connection String"))
{
conn.Open();
SqlTransaction trans;
trans = conn.BeginTransaction();
string selectQuery = "your sql query";
SqlCommand command = new SqlCommand(selectQuery, connection);
int iResult = command.ExecuteNonQuery();
if (iResult > 0)
{
trans.Commit();
}else{
trans.Rollback();
}
conn.Close();
}
Use a formatted string for your select query by using # and the syntax in the value block in not accurate.
string selectQuery = #"
BEGIN TRANSACTION
UPDATE tester SET test_ad = #dateTimePicker1, test_ud = #dt, test_pd = #dt WHERE test_name = #textBox1;
INSERT INTO records(testr_status, testr_name, testr_ad, testr_ud, testr_pd, apte_name) VALUES(#testr_status, #comboBox1, #dateTimePicker1, #dt, #dt);
COMMIT";
Related
I am trying to return a value from the code below but I am getting an error that says:
A SqlParameter with parameter name '#vRESULT' is not contained by this SqlParameterCollection
c# Code:
public int userLogin()
{
string connStr = ConfigurationManager.ConnectionStrings["conn"].ToString();
string cmdStr = #"fucn_LOg";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(cmdStr, conn))
{
try
{
conn.Open();
cmd.CommandText = cmdStr;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters[":vResult"].Direction = ParameterDirection.Output;
cmd.Parameters.Add(new SqlParameter("param1", SqlDbType.VarChar)).Value = TB_1.Text;
cmd.Parameters.Add(new SqlParameter("param2", SqlDbType.VarChar)).Value = TB_2.Text;
cmd.ExecuteScalar();
return Int32.Parse(cmd.Parameters[":vResult"].Value.ToString());
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return -1;
}
}
}
the sql server function code below with returning parameter DECLARE #vResult int
CREATE FUNCTION USER_LOGIN(#USER_NAME VARCHAR(60),
#PWD VARCHAR(60))
RETURNS INT
AS BEGIN
DECLARE #vResult int
SELECT #vRESULT=COUNT(*)
FROM OPER
WHERE UPPER(UNAM)=UPPER(#USER_NAME)
AND PSW=#PWD
IF #vResult=1
SET #vResult=1
ELSE
SET #vResult= -1
RETURN #vResult
END
Just Get result from Stroed Procedure like this:
var result = cmd.ExecuteScalar();
return Int32.Parse(result.ToString());
This gets first and Only result from Stored Procedure.
Also recommend simplify your code Like this:
public int userLogin() {
string connStr = ConfigurationManager.ConnectionStrings["conn"].ToString();
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand("fucn_LOg", conn)) {
try {
cmd.Connection.Open();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#param1", TB_1.Text);
cmd.Parameters.AddWithValue("#param2", TB_2.Text);
var result = cmd.ExecuteScalar();
return Int32.Parse(result.ToString());
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
return -1;
}
finally {
if (cmd.Connection.State != ConnectionState.Closed) cmd.Connection.Close();
}
}
}
And your Stored procedure should looks Like this:
CREATE PROCEDURE fucn_LOg
(
#param1 nvarchar(max),
#param2 nvarchar(max)
)
AS
BEGIN
SET NOCOUNT ON;
if (exists(select * from tbUsers where flLogin = #param1 and flPassword = #param2))
begin
return 1;
end
else
begin
return 0;
end
END
GO
OR
CREATE PROCEDURE fucn_LOg
(
#param1 nvarchar(max),
#param2 nvarchar(max)
)
AS
BEGIN
SET NOCOUNT ON;
select COUNT(*) from tbUsers where flLogin = #param1 and flPassword = #param2
END
GO
Several problems.
First, you don't need the cmd.Parameters.Clear();, as you just establish a new cmd.
Second, use # for SQL Server parameters.
Third, a parameter named vResult is not set, so cmd.Parameters[":vResult"].Direction is invalid. You need to assign its type and value. Make sure your stored procedure has this parameter set with correct SQL data type.
Lastly, I guess you return the vResult in your stored procedure like select #vResult; so make it a new vResult = function(vResult). But no, it is not how SQL Server work. It won't change your input parameter even though you return your #vResult. While, ExecuteScaler does. So, simply get your result back by var result = cmd.ExecuteScalar();.
You are getting data from a stored procedure, not getting back the parameter you sent. That's the supposed correct way.
conn.Open();
cmd.CommandText = cmdStr;
cmd.CommandType = CommandType.StoredProcedure;
//Base on sql you provided, it is no need for this part.
/*
SqlParameter vResult = new SqlParameter();
vResult.ParameterName = "#vResult";
vResult.Direction = ParameterDirection.Output;
vResult.SqlDbType = System.Data.SqlDbType.???;
vResult.Value = ???;
cmd.Parameters.Add(vResult);
*/
cmd.Parameters.Add("#param1", SqlDbType.VarChar).Value = TB_1.Text;
cmd.Parameters.Add("#param2", SqlDbType.VarChar).Value = TB_2.Text;
var result = cmd.ExecuteScalar();
return Int32.Parse(result.ToString());
This is hard to debug without the SP, but a couple of things jump out.
First, you need to use the '#' character as a prefix for your parameter names, not a colon.
Second, you should define your output parameter like this:
SqlParameter outputParam = new SqlParameter("#vResult", SqlDbType.Int);
outputParam.Direction = ParameterDirection.Output;
cmd.Parameters.Add(outputParam);
I'm using the ExecuteNonQuery function and stored procedure to insert a new record in an MSSQL database.
During testing the insert of the new record is successful. But my second call to ExecuteScalar and get the newly inserted record's ID fails. The reason for the failure according to the debugger is:
ExecuteScalar requires an open and available Connection. The
connection's current state is closed.
Looking at this error it explains that the connection has been closed after the initial call to ExecuteNonQuery. Meaning that my code to get the ID won't have a valid connection to query with.
Question:
How can you retrieve ##Identity following an ExecuteNonQuery?
This is the piece of code that performs the insert in the DAL:
Database db = GetConnectionString();
string sqlCommand = "CREATE_RECORD";
string idQuery= "Select ##Identity";
int recID = 0;
using (DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand))
{
db.AddInParameter(dbCommand, "#Application", DbType.String, escalation.Application);
db.AddInParameter(dbCommand, "#UpdatedTime", DbType.DateTime, escalation.UpdatedTime);
db.ExecuteNonQuery(dbCommand);
dbCommand.CommandText = idQuery;
recID = (int)dbCommand.ExecuteScalar();
return recID ;
}
DISCLAIMER: This is a bad idea - the correct solution is server-side (server in this case is SQL Server).
You may be able to do this if you use SCOPE_IDENTITY() (which you should anyway - ##IDENTITY is not guaranteed to be your insert's identity) and execute your command as CommandType.Text instead of CommandType.StoredProcedure
WARNING: Serious security implications here, most notably SQL Injection Attack possibility:
Database db = GetConnectionString();
string sqlCommand = $"CREATE_RECORD '{escalation.Application}', '{escalation.UpdatedTime}'";
string idQuery= "Select SCOPE_IDENTITY()"
int recID = 0;
using (DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand))
{
dbCommand.CommandType = commandType.Text;
db.ExecuteNonQuery(dbCommand);
dbCommand.CommandText = idQuery;
recID = (int)dbCommand.ExecuteScalar();
return recID;
}
Of course, if you go this route, you might as well combine both commands into a single query:
Database db = GetConnectionString();
string sqlCommand = $"CREATE_RECORD '{escalation.Application}', '{escalation.UpdatedTime}'; Select SCOPE_IDENTITY()";
int recID = 0;
using (DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand))
{
dbCommand.CommandType = commandType.Text;
//TODO: Open connection using your db object
recID = (int)dbCommand.ExecuteScalar();
//TODO: Close connection using your db object
return recID;
}
Again, I stress that the correct solution is to fix this in SQL, not in C#. Use at your own risk!
You should create and open connection for each query and dispose it after query. Don't worry, there are connection pool in ADO and connection will not be physically established and closed each time. It's only a hint for ADO.NET.
int recID = 0;
string connStr = ProcThatGivesYouConnectionString();
using (SqlConnection con = new SqlConnection(connStr))
{
con.Open();
SqlCommand command = new SqlCommand("CREATE_RECORD", con);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#Application", escalation.Application);
command.Parameters.AddWithValue("#UpdatedTime", escalation.UpdatedTime);
command.ExecuteNonQuery();
}
using (SqlConnection con2 = new SqlConnection(connStr))
{
con2.Open();
SqlCommand command = new SqlCommand("Select ##Identity", con2);
recID = (int)command.ExecuteScalar();
}
Also you can execute both queries in one command if you want:
int recID = 0;
string connStr = ProcThatGivesYouConnectionString();
using (SqlConnection con = new SqlConnection(connStr))
{
con.Open();
SqlCommand command = new SqlCommand("
EXEC CREATE_RECORD #Application = #Application, #UpdatedTime = #UpdatedTime
SELECT ##Identity", con);
command.Parameters.AddWithValue("#Application", escalation.Application);
command.Parameters.AddWithValue("#UpdatedTime", escalation.UpdatedTime);
recID = (int)command.ExecuteScalar();
}
object r = command.ExecuteScalar();
Convert.ToInt32(r.ToString());
To prevent the ExecuteScalar gets Specified cast is not valid error , use above
The following code executing a bunch of SQL statements works well...
// SQL Server 2008 R2
SqlConnection connection = null;
var runBatch = false;
try
{
connection = new SqlConnection(connectionString);
connection.Open();
var command = connection.CreateCommand();
// 1st batch
command.CommandText = "BEGIN TRANSACTION";
command.ExecuteNonQuery;
// 2nd batch
command.CommandText = "UPDATE MyTable SET MyColumn = 'foo' WHERE Name = 'bar'";
command.ExecuteNonQuery;
// 3rd batch
command.CommandText = "COMMIT TRANSACTION";
command.ExecuteNonQuery;
}
finally
{
if (connection != null)
{
connection.Close();
}
}
...unless I use a parameter:
// [...]
command.Parameters.AddWithValue("name", "bar");
// 1st batch
command.CommandText = "BEGIN TRANSACTION";
command.ExecuteNonQuery; // <= throws Exception
Exception message:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0
Note: This would work, if I combined all the statements in a single command. But in my app, the original SQL script is a bunch of batches separated by GO which is automatically split into multiple commands (so I have no control over how or what transactions are used):
/* Original SQL */
BEGIN TRANSACTION
GO
UPDATE MyTable
SET MyColumn = 'foo'
WHERE NAME = #name
GO
COMMIT TRANSACTION
GO
I have read about this exception, but nothing really seems to apply to this special scenario.
I have not figured out yet what exact difference introducing a parameter makes here and why it breaks the code. Anyone got a solution for this?
Try this:
// SQL Server 2008 R2
SqlConnection connection = null;
var runBatch = false;
try
{
connection = new SqlConnection(connectionString);
connection.Open();
var command = connection.CreateCommand();
// 1st batch
command.CommandText = "BEGIN TRANSACTION";
command.ExecuteNonQuery();
// 2nd batch
command.CommandText = "UPDATE MyTable SET MyColumn = 'foo' WHERE Name = #name";
command.Parameters.AddWithValue("name", "bar");
command.ExecuteNonQuery();
// 3rd batch
command.CommandText = "COMMIT TRANSACTION";
command.Parameters.Clear();
command.ExecuteNonQuery();
}
finally
{
if (connection != null)
{
connection.Close();
}
}
I'm getting a syntax error in INSERT INTO statement and I can't figure out why. I've checked several different SO questions that were almost exactly the same as my problem and after changing my code this way and that way it still isn't working.
var cnnStr = String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};", oldDb);
var cnn = new OleDbConnection(cnnStr);
cnn.Open();
//make new access table
using (OleDbCommand command = new OleDbCommand())
{
command.Connection = cnn;
command.CommandText = String.Format("CREATE TABLE [{0}] ([Tag] string, [Text] string)", newTable + "_Diff");
try
{
command.ExecuteNonQuery();
}
catch
{
//table already exists
}
}
//fill access table
using (OleDbCommand command = new OleDbCommand())
{
command.Connection = cnn;
command.CommandText = String.Format("INSERT INTO [{0}] (Tag, Text) VALUES (?, ?)", newTable + "_Diff");
command.Parameters.Add(new OleDbParameter("Tag", ""));
command.Parameters.Add(new OleDbParameter("Text", ""));
for (int i = 0; i < (diffText.Length - 1); i++)
{
command.Parameters["Tag"].Value = diffTag[i];
command.Parameters["Text"].Value = diffText[i];
command.ExecuteNonQuery();
}
}
cnn.Close();
Creating the table is working so I know there's not a problem with my connection, there's just something it doesn't like about my insert statement.
In your insert command put text inside a square bracket, "text" is a keyword
command.CommandText = String.Format("INSERT INTO [{0}] (Tag, [Text])
VALUES (?, ?)", newTable + "_Diff");
also make sure you are including your values with single quote
values ('?','?')
hope this works
I am having trouble updating data into a table from my C# form. Could someone tell me what I am doing wrong. It does not give me an error and says that it has been submitted successfully. My database show no change however.
try
{
string cmdstring = "UPDATE Test SET COLLECTION_DATE = '#date', SPECIMANID = '#spec', TEST_RESULT = '#result', SUBSTANCE_RESULT = '#substance' WHERE PEOPLESOFT_EMPL_ID = #empID ;";
using (OleDbCommand cmd = new OleDbCommand(cmdstring, con))
{
cmd.Parameters.Add("#date", OleDbType.Date).Value = dateTimePicker1.Value;
cmd.Parameters.Add("#spec", OleDbType.Char).Value = textBoxSpec.Text;
cmd.Parameters.Add("#result", OleDbType.Char).Value = comboBoxResult.Text;
cmd.Parameters.Add("#substance", OleDbType.Char).Value = comboBoxSubstance.Text;
cmd.Parameters.Add("#empID", OleDbType.Char).Value = textBoxID.Text;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
MessageBox.Show("Submitted Successfully");
}
}
catch (Exception ex)
{
MessageBox.Show("Failed due to " + ex.Message);
}
Problem : You are enclosing the Parameters in your update query within single quotes.
Solution : while using parameterised queries you should not enclose the parameters within single quotes, so you need to remove the single quotes around the parameters in update query.
Try This:
string cmdstring = "UPDATE Test SET COLLECTION_DATE = #date, SPECIMANID = #spec,
TEST_RESULT = #result, SUBSTANCE_RESULT = #substance WHERE PEOPLESOFT_EMPL_ID =
#empID ;";
Suggestion: You are displaying the success mesaage without checking the query execution status.
i would suggest you to verify the ExecuteNonQuery() return value before displaying the Success message.
From MSDN :ExecuteNonQuey()
Executes a Transact-SQL statement against the connection and returns
the number of rows affected.
Try This:
int rowsUpdated = cmd.ExecuteNonQuery();
if(rowsUpdated > 0)
MessageBox.Show("Records Inserted Successfully!");
else
MessageBox.Show("Insertion Failed!");