Sql transaction c# - exits try when ExecuteNonQuery() is run - c#

I'm trying to implement an sql transaction, however, i've run into a bit of bother. I have a simple database with has three tables. Users, Post and Comments. I wish to implement a delete button that will delete a user from the Users table. The initial problem I had was that I needed to remove the users FK from both the post and comment table, then remove the user from the User table. I looked online and somebody had suggested using transaction for multiple server calls. Here is my code:
public void DeleteUser(int UserId)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
SqlTransaction sqlTran = con.BeginTransaction();
try
{
using (SqlCommand command = new SqlCommand("DELETE FROM Comment WHERE UserId = " + #UserId, con))
{
SqlParameter userid = new SqlParameter("#UserId", SqlDbType.Int);
userid.Value = UserId;
command.ExecuteNonQuery();
}
using (SqlCommand command = new SqlCommand("DELETE FROM Post WHERE UserId = " + #UserId, con))
{
SqlParameter userid = new SqlParameter("#UserId", SqlDbType.Int);
userid.Value = UserId;
command.ExecuteNonQuery();
}
using (SqlCommand command = new SqlCommand("DELETE FROM Users WHERE UserId = " + #UserId, con))
{
SqlParameter userid = new SqlParameter("#UserId", SqlDbType.Int);
userid.Value = UserId;
command.ExecuteNonQuery();
}
sqlTran.Commit();
}
catch
{
sqlTran.Rollback();
}
}
}
The problem I have is that when the DeleteUser method is run, the program gets as far as the command.ExecutyNonQuery in the first using block, then jumps to the catch. Thus rolling back the changes. There isn't any error codes displayed, so i'm not sure what is going wrong.
Any help in this matter would be greatly appreciated.

You're missing command.Transaction = sqlTran;.
DO NOT USE try {} catch {}. Use at least try {} catch (Exception ex) {} -> this will give you enough information to solve the problem on your own.

Related

Delete a record from database

I'm trying to delete record from data base MSSQL by entering the ID and hit delete btn. i didn't get any error and it give recorded deleted successful but once i check database i see the record doesn't deleted
protected void btnDelete_Click(object sender, EventArgs e)
{
try
{
if (txtImgID.Text == "")
{
Response.Write("Enter Image Id To Delete");
}
else
{
SqlCommand cmd = new SqlCommand();
SqlConnection con = new SqlConnection();
con = new SqlConnection(ConfigurationManager.ConnectionStrings["GMSConnectionString"].ConnectionString);
con.Open();
cmd = new SqlCommand("delete from certf where id=" + txtImgID.Text + "", con);
lblsubmitt.Text = "Data Deleted Sucessfully";
}
}
catch (Exception)
{
lblsubmitt.Text = "You haven't Submited any data";
}
}
var idToDelete = int.Parse(txtImgID.Text); // this is not necessary if the data type in the DB is actually a string
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["GMSConnectionString"].ConnectionString))
using (SqlCommand cmd = new SqlCommand("DELETE FROM [certf] WHERE id = #id", con))
{
// I am assuming that id is an integer but if it is a varchar/string then use the line below this one
// cmd.Parameters.Add("#id", SqlDbType.VarChar, 100).Value = txtImgID.Text;
cmd.Parameters.Add("#id", SqlDbType.Int32).Value = idToDelete;
cmd.ExecuteNonQuery();
}
You need to call ExecuteNonQuery which executes the query against the database.
Always use parameters instead of string concatenation in your queries. It guards against sql injection and ensures you never has issues with strings that contain escape characters.
I did not include any error handling or return messages but do note that you are throwing away all the good stuff in your excetion handler's catch block, you will never know why a query failed after this has executed.

How set multiple editing of data in SQL Server from asp.net

I have simple asp.net web app. That has multiple users who can login and edit Info. However, I have noticed when users are updating info at the same time.When data goes to the MS SQL DB the data is swapped.
Example
UserA was editing Item1, while UserB was editing Item2. On updating the info, it shows UserA was editing Item2.
On login User's Credentials are stored in a session
HttpContext.Current.Session.Add("Username", rs["Username"].ToString());
Code below is used to call the procedure in DB
using (SqlConnection con = new SqlConnection(dc.Con)) {
using (SqlCommand cmd = new SqlCommand("sp_EditItem", con)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemName", SqlDbType.VarChar).Value = txtItemInfo.Text;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = txtItemID.Text;
con.Open();
cmd.ExecuteNonQuery();
}
}
Query in the procedure
Update tb_Items
set ItemName = #ItemName
where ItemID = #ItemID
When a single user is updating the data, data is updated correctly.
What causes data to be swapped when multiple users are editing info?
Use SQL Transaction and modify code as below
using (SqlConnection con = new SqlConnection(dc.Con)) {
using (SqlCommand cmd = new SqlCommand("sp_EditItem", con)) {
using (SqlTransaction transaction = connection.BeginTransaction("SampleTransaction"))
{
try
{
cmd.Transaction = transaction;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemName", SqlDbType.VarChar).Value = txtItemInfo.Text;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = txtItemID.Text;
con.Open();
cmd.ExecuteNonQuery();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
}
}
}

Insert using Scope Identity as Attribute

When creating User, I need to give it a system Role, so I was wondering what is the best way to do that.
I have working solution, but I'm unsure if it's the best way.
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand(#"INSERT INTO Users (Name, Email, Username, Password, Active) VALUES (#Name, #Email, #Username, #Password, #Active); SELECT SCOPE_IDENTITY();", con))
{
try
{
cmd.Parameters.AddWithValue("#Name", user._Name);
cmd.Parameters.AddWithValue("#Email", user._Email);
cmd.Parameters.AddWithValue("#Username", user._Email);
cmd.Parameters.AddWithValue("#Password", user.Password);
cmd.Parameters.AddWithValue("#Active", 1);
user_id = Convert.ToInt32(cmd.ExecuteScalar());
//cmd.ExecuteNonQuery();
}
catch (SqlException)
{
//Handle Exception
}
}
}
using (SqlConnection con = new SqlConnection(conString))
{
using (SqlCommand cmd = new SqlCommand("INSERT INTO User_Role (User_Role_User_Id, User_Role_Role_Id) VALUES (#User_ID, #Role_ID)", con))
{
try
{
cmd.Parameters.AddWithValue("#User_ID", user_id);
cmd.Parameters.AddWithValue("#Role_ID", user.Role_ID);
cmd.ExecuteNonQuery();
}
catch (SqlException)
{
//Handle Exception
}
}
}
Now my problem is, if adding the role goes wrong for whatever reason, I'll have a user created without a role.
So I was wondering if it's possible to join the two SqlCommand, considering that I need the Scope_Identity for the insert into User_Role. Or do a rollback on the last exception catch for both Insert's?
Either make a stored procedure that does the work as an atomic unit, or if you are unable to do that, you can wrap the entire code block in a transaction if yo keep using the same connection, like this:
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
var transaction = con.BeginTransaction();
try
{
//Run first command
//Run second command
//If we have succeeded, commit the transaction
transaction.Commit();
}
catch()
{
//Something went wrong, roll back
transaction.Rollback();
}
}

How to insert data into a database table using a SqlCommand

I am trying to insert data into a database that I have that has a table called EmployeeInfo
The user is prompted to enter a last name and select a department ID (displayed to the user as either marketing or development) The column ID automatically increments.
Here is my Code behind
protected void SubmitEmployee_Click(object sender, EventArgs e)
{
var submittedEmployeeName = TextBox1.Text;
var submittedDepartment = selectEmployeeDepartment.Text;
if (submittedEmployeeName == "")
{
nameError.Text = "*Last name cannot be blank";
}
else
{
System.Data.SqlClient.SqlConnection sqlConnection1 =
new System.Data.SqlClient.SqlConnection("ConnString");
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "INSERT INTO EmployeeInfo (LastName, DepartmentID ) VALUES ('" + submittedEmployeeName + "', " + submittedDepartment + ")";
cmd.Connection = sqlConnection1;
sqlConnection1.Open();
cmd.ExecuteNonQuery();
sqlConnection1.Close();
}
}
The error I'm recieving is 'Arguement exception was unhandled by user code'
Here is a picture of it.
As requested. More details
If I had enough reputation, I would rather post this as a reply, but it might actually be the solution.
The reason why it stops there is because you are not providing a legit SqlConnection, since your input is: "ConnString", which is just that text.
The connection string should look something like:
const string MyConnectionString = "SERVER=localhost;DATABASE=DbName;UID=userID;PWD=userPW;"
Which in your case should end up like:
System.Data.SqlClient.SqlConnection sqlConnection1 = new System.Data.SqlClient.SqlConnection(MyConnectionString);
Besides that, you should build your connections like following:
using (SqlConnection con = new SqlConnection(MyConnectionString)) {
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = xxxxxx; // Your query to the database
cmd.Connection = con;
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
}
This will do the closing for you and it also makes it easier for you to nestle connections. I did a project recently and did the connection your way, which ended up not working when I wanted to do more than one execute in one function. Just important to make a new command for each execute.

Insert/Update in asp.net and SQL SERVER 2008

I am trying to insert a record if requested prodName doesnot exist in database. If it exists I want to update the value of quantity attribute. I have used the following it neither inserts nor Updates any record. I get following exception:
ExecuteScalar requires an open and available Connection. The connection's current state is closed
This is the code
public static void manageStock(CompanyStock stock)
{
///// Check if record exists/////////
cmd = new SqlCommand("select count(*) from tblStock where prodName=#prodName", con);
cmd.Parameters.AddWithValue("#prodName", stock.prodName);
con.Open();
Int32 count = (Int32)cmd.ExecuteScalar(); //returns null if doesnt exist
con.Close();
if (count > 0)
{
cmd = new SqlCommand("update tblStock set quantity = #quantity where prodName=#prodName", con);
cmd.Parameters.AddWithValue("#prodName", stock.prodName);
cmd.Parameters.AddWithValue("#quantity", stock.quantity);
}
else
{
cmd = new SqlCommand("insert into tblStock(prodName,quantity) values (#prodName, #quantity)", con);
cmd.Parameters.AddWithValue("#prodName",stock.prodName);
cmd.Parameters.AddWithValue("#quantity",stock.quantity);
}
try
{
con.Open();
cmd.ExecuteNonQuery();
}
finally
{
con.Close();
}
}
}
Edited
I edited my code. It works fine now. I had to open my connection before executing ExecuteScalar But I want to know the standard way of writing this opening and closing stuff. It looks kind of haphazard. How can I improve this?
You can use Convert.ToInt32() method for converting the result into integer value.
if the value is null it converts it into 0.
Try This:
int count = Convert.ToInt32(cmd.ExecuteScalar());
Consider using MERGE clause in sql-server. Here is a good Microsoft article you can use.
What does it do when you step through the code?
In some SQL collations (Latin1_General_BIN for example), variables are case sensitive. In your first select statement you have #ProdName in your query and #prodName in your parameters collection. If you have a case sensitive collation, you're never getting past this part. Right-click on the database in Management Studio and click Properties to find the collation.
Error say that there's no connection.May u check first of all that issue so
Check connection and if is not null and exist at this point check it con.State = Open or any other value. I connection state is closed open it.But first of all where is connections declaration ? i don't see it in your code.
TRY THIS :
//USING THE STATEMNET USING IT WILL TAKE CARE TO DISPOSE CONNECTION AND PLACE TRY CATCH WITHIN PROCS
{
using (SqlConnection cnn = new SqlConnection(ConfigurationManager.AppSettings("connectionString"))) {
if (cnn.State == System.Data.ConnectionState.Closed)
cnn.Open();
using (SqlCommand cmd = new SqlCommand()) {
try {
cmd.Connection = cnn;
cmd.CommandText = "YOUR SQL STATEMENT";
int I = Convert.ToInt32(cmd.ExecuteNonQuery);
if (I > 0)
{
cmd.CommandText = "YOUR SQL STATEMENT";
//ADDITIONAL PARAMTERES
}
else
{
cmd.CommandText = "YOUR SQL STATEMENT";
//ADDITIONAL PARAMETERS
}
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}
}
}
You can try this code. First write a stored procedure:
CREATE PROCEDURE sprocquanupdateinsert
#prodName nvarchar(250),
#quantity int
AS
BEGIN
UPDATE tblStock
SET quantity = #quantity
WHERE prodName = #prodName
IF ##ROWCOUNT = 0
INSERT INTO tblStock(prodName, quantity)
VALUES (#prodName, #quantity)
END
GO
Then in code behind you can use this
using (conn)
{
SqlCommand cmd = new SqlCommand("sprocquanupdateinsert", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#prodName", stock.prodName);
cmd.Parameters.AddWithValue("#quantity", stock.quantity);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}

Categories