deadlock error when try to update from gridview - c#

Transaction (Process ID 588) was deadlocked on lock resources with
another process and has been chosen as the deadlock victim. Rerun the
transaction.
i get that error when i try to update data from datagridview how can i solve it or what is the problem with my update code and thank you ,
private void Button2_Click(object sender, EventArgs e)
{
using (SqlConnection con = new SqlConnection("***"))
{
con.Open();
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
// INSERT command:
using (SqlCommand com = new SqlCommand("UPDATE tabl2 SET TEL8=#TEL8 WHERE id=#id and CIVILIDD=#CIVILIDD ", con))
{
com.Parameters.AddWithValue("#id", dataGridView1.Rows[i].Cells[0].Value);
com.Parameters.AddWithValue("#CIVILIDD", dataGridView1.Rows[i].Cells[1].Value);
com.Parameters.AddWithValue("#TEL8", dataGridView1.Rows[i].Cells[2].Value.ToString());
com.ExecuteNonQuery();
}
}
MessageBox.Show("Successfully UPDATE....");
}
}
sql server table :
id = int
CIVILIDD = bigint
TEL8 = nvarchar (MAX)

try this if this will work
using (SqlCommand com = new SqlCommand("UPDATE tabl2 SET TEL8=#TEL8 WHERE id=#id and CIVILIDD=#CIVILIDD ", con))
{
com.Parameters.Add("#id", SqlDbType.Int);
com.Parameters.Add("#CIVILIDD", SqlDbType.BigInt);
com.Parameters.Add("#TEL8", SqlDbType.NVarChar);
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
com.Parameters["#id"].Value = dataGridView1.Rows[i].Cells[0].Value);
com.Parameters["#CIVILIDD"].Value = dataGridView1.Rows[i].Cells[1].Value);
com.Parameters["#TEL8"].Value = dataGridView1.Rows[i].Cells[2].Value.ToString());
}
com.ExecuteNonQuery();
}
or
alter your db to single_user before running the update then reverting it back to multi_user
new SqlCommand("Alter Database {databaseName} SET SINGLE_USER With ROLLBACK IMMEDIATE", con);
//your update commands
new SqlCommand("Alter Database {databaseName} SET MULTI_USER With ROLLBACK IMMEDIATE", con);
also for this to occur,
check all sql connection and command creation, if all of them are using using SqlCommand and using SqlConnection. I believe this might be the reason for it to fail as a connection and command are not yet dispose.
maybe a very busy server, research on commandtimeout and connectiontimeout, commandtimeout setting will cause your query to wait up until the query is executed if set to 0. connectiontimeout will cause your connection attempt to wait up until it has made a connection if set to 0.

Related

Second query failed to update because first query still in insert progress

private void btnSave_Click(object sender, EventArgs e)
{
using (OleDbConnection con = new OleDbConnection(cs))
{
con.Open();
cmd = new OleDbCommand(“INSERT INTO table1 ([name], [gender], [age]) VALUES ('Jeff', 'Male', 51), con);
cmd.ExecuteNonQuery();
//System.Threading.Thread.Sleep(1000); // it's working if i add a delay here
//int success = cmd.ExecuteNonQuery(); // also working if check number of query affected
//if (success > 0)
//{
// updateLastModified();
//}
updateLastModified();
}
}
public void updateLastModified()
{
using (OleDbConnection con = new OleDbConnection(cs))
{
con.Open();
cmd = new OleDbCommand("UPDATE TABLE1 SET LastModifiedTime='" + DateTime.Now.ToString() + "' WHERE name='Jeff'", con);
cmd.ExecuteNonQuery();
// this was not updated because "Jeff" cannot be found in table1 (first insert query still running)
}
}
My problem was second query not updated because first query still running.
Any better solution other than "adding a delay" or "check first query was successful" before perform second query?
This is just an example scenario, I'm not going to do in one query.
Update:
Suggestion from #a.rlx was using OleDbTransaction.Commit method. Can i do by this way without using try catch?
using (OleDbConnection con = new OleDbConnection(cs))
{
OleDbTransaction transaction = null;
con.Open();
transaction = con.BeginTransaction();
cmd = new OleDbCommand(“INSERT INTO table1 ([name], [gender], [age]) VALUES ('Jeff', 'Male', 51), con, transaction);
cmd.ExecuteNonQuery();
transaction.Commit();
updateLastModified();
}
Have you tried to start a transaction, run both SQL statements, and then commit the transaction?
https://dev.mysql.com/doc/dev/connector-net/6.10/html/M_MySql_Data_MySqlClient_MySqlConnection_BeginTransaction.htm

.NET MySqlCommand.Transaction - what is its purpose?

When using the ADO.Net driver for MySQL in .NET, is it necessary to assign a transaction to the MySqlCommand object (for example, using oCmd.Transaction = oTran) or is it enough that the MySqlConnection object has an open transaction? In the code below I begin a transaction on the connection, run two queries with separate MySqlCommand objects without assigning a transaction and then roll back the transaction. In this example, neither UPDATE is committed to the database.
MySqlConnection oConn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;");
oConn.Open();
MySqlTransaction oTran = oConn.BeginTransaction();
MySqlCommand oCmd = oConn.CreateCommand();
oCmd.CommandText = "UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 1";
oCmd.ExecuteNonQuery();
oCmd = oConn.CreateCommand();
oCmd.CommandText = "UPDATE testing SET testBalance = testBalance - 10 WHERE testID = 2";
oCmd.ExecuteNonQuery();
oTran.Rollback();
oConn.Close();
When checking the Transaction property of oCmd at runtime, I see that it is null.
Obviously, if I call oTran.Commit() then both UPDATE statements are committed. So what is the purpose of the Transaction property of the MySqlCommand object? Is it to allow more than one concurrent transaction on a single connection (where different commands would be bound to different transactions and could be rolled back or committed irrespective of each other)?
For a single statement, you can pretend the property doesn't exist, along with the Commit() and Rollback() methods. If the property is not there, individual statements are auto-committed. In the code from the question, it would be possible to run a query in the brief span between when testID=1 is updated and testID=2 is updated, and the Rollback() method won't accomplish anything.
To take advantage of the MySqlTransaction object, you need to do this:
using (var Conn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;"))
{
Conn.Open();
MySqlTransation Tran = Conn.BeginTransaction();
using (var Cmd = new MySqlCommand("UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 1", Conn))
{
Cmd.Transaction = Tran;
Cmd.ExecuteNonQuery();
}
using (var Cmd = new MySqlCommand("UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 2", Conn))
{
Cmd.Transaction = Tran;
Cmd.ExecuteNonQuery();
}
Tran.Rollback();
}
Or even better:
using (var Conn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;"))
using (var Cmd = new MySqlCommand("UPDATE testing SET testBalance = testBalance + 10 WHERE testID = #testID", Conn))
{
Conn.Open();
Cmd.Transaction = Conn.BeginTransaction();
Cmd.Parameteres.Add("#testID", MySqlDbType.Int32).Value = 1;
Cmd.ExecuteNonQuery();
Cmd.Parameters["testID"].Value = 2; //I can't remember at the moment if you need the "#" here or not
Cmd.ExecuteNonQuery();
Cmd.Transaction.Rollback();
}
You need transactions mainly when you want to guarantee multiple operations are atomic. This works when you assign the transaction to each instance of the command object individually, each instance of the command uses the same connection, and the connection is held open for the duration of the transaction.
Additionally, you can put multiple statements into a single command object:
string sql =
"BEGIN;" +
"UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 1;" +
"UPDATE testing SET testBalance = testBalance - 10 WHERE testID = 2;" +
"COMMIT;";
using (var Conn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;"))
using (var Cmd = new MySqlCommand(sql, Conn))
{
Conn.Open();
Cmd.ExecuteNonQuery();
}
This is the preferred method, but sometimes the nature of your code will prevent it, and you'll have to use the MySqlTransaction object instead.

How to retrieve ##Identity following an ExecuteNonQuery?

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

SQL Output Inserted

I have two buttons on a page, one that logs a start time, and one that logs an end time.
The start time button performs an sql insert.
At that point i need to grab the primary key that's create. To do this i want to use the sql command (output inserted).
Then when the stop time is clicked,the row should be update with a stop time using the primary key from the start in the where clause.
I believe insert SQL is correct but i don't know how to pass the primary key to the next command.
Code dump, with what i have so far.
var command1 = "INSERT INTO [Time] ([Start Time], [Work Order]) OUTPUT INSERTED.PrimaryKey VALUES (#StartTime, #Work_Order)";
using (SqlConnection cnn1 = new SqlConnection(cnnString))
{
using (SqlCommand cmd1 = new SqlCommand(command1, cnn1))
{
cmd1.Parameters.AddWithValue("#StartTime", SqlDbType.DateTime).Value = System.DateTime.Now;
cmd1.Parameters.AddWithValue("#Work_Order", SqlDbType.Int).Value = e.CommandArgument;
cnn1.Open();
Label1.Text = cmd1.ExecuteScalar().ToString();
cnn1.Close();
}
}
var command = "UPDATE [Time] SET [Stop Time] = #StopTime WHERE [PrimaryKey] = #PrimaryKey";
using (SqlConnection cnn = new SqlConnection(cnnString))
{
using (SqlCommand cmd = new SqlCommand(command, cnn))
{
cmd.Parameters.AddWithValue("#StopTime", SqlDbType.DateTime).Value = System.DateTime.Now;
cmd.Parameters.AddWithValue("#PrimaryKey", *PrimaryKey from INSERT output*
cnn.Open();
cmd.ExecuteNonQuery();
}
}
instead of having it go to the label have it go to an int and then set the label text with the int. Then pass the int on the second part. Declare the int outside the scope of the using statements though or it will be disposed and you will get a null reference exception when you try and call it later.
Edit: To add, this would be better if you convert to stored procs and define the SqlParameter objects (you don't have them, you'll need them).
SqlParameter
int myPK;
var command1 = "INSERT INTO [Time] ([Start Time], [Work Order]) OUTPUT INSERTED.PrimaryKey VALUES (#StartTime, #Work_Order)";
using (SqlConnection cnn1 = new SqlConnection(cnnString))
{
using (SqlCommand cmd1 = new SqlCommand(command1, cnn1))
{
cmd1.Parameters.AddWithValue("#StartTime", SqlDbType.DateTime).Value = System.DateTime.Now;
cmd1.Parameters.AddWithValue("#Work_Order", SqlDbType.Int).Value = e.CommandArgument;
cnn1.Open();
myPk = Convert.ToInt32(cmd1.ExecuteScalar());
Label1.Text = myPk.ToString();
cnn1.Close();
}
}
var command = "UPDATE [Time] SET [Stop Time] = #StopTime WHERE [PrimaryKey] = #PrimaryKey";
using (SqlConnection cnn = new SqlConnection(cnnString))
{
using (SqlCommand cmd = new SqlCommand(command, cnn))
{
cmd.Parameters.AddWithValue("#StopTime", SqlDbType.DateTime).Value = System.DateTime.Now;
cmd.Parameters.AddWithValue("#PrimaryKey", myPK);
FindControl("Work_OrderLabel"); ;
cnn.Open();
cmd.ExecuteNonQuery();
}
}

Trying to make two SQL INSERT statements atomic in C# code

Below is the code I'm using in an SSIS script task. I am trying to make both inserts atomic as they deal with similar customers.
The first .executeNonQuery() works fine, locking the SQL table as it should.
The second .executNonQuery() throws an error:
ExecuteNonQuery requires the command to have a transaction when the
connection assigned to the command is in a pending local transaction.
The Transaction property of the command has not been initialized.
Code:
ConnectionManager cm;
SqlTransaction sqlTrans;
SqlConnection sqlConn;
SqlCommand sqlComm;
cm = Dts.Connections["connectionManager"];
try
{
//Set 'global' variables
SqlParameter agentID = new SqlParameter("#agentID", 1000018); //retrievedMessage.Substring(2, 10));//Primary key
SqlParameter lastChangeOperator = new SqlParameter("#lastChangeOperator", "LVO");
SqlParameter lastChangeDate = new SqlParameter("#lastChangeDate", DateTime.Now);
SqlParameter controlId = new SqlParameter("#controlID", 1); //Hard-coded value for testing - CHANGE LATER
//Set variables for Agent table
SqlParameter entityType = new SqlParameter("#entityType", "P");//retrievedMessage.Substring(162, 1));
SqlParameter fName = new SqlParameter("#fName", "test");//retrievedMessage.Substring(12, 25));
SqlParameter lName = new SqlParameter("#lName", "test");//retrievedMessage.Substring(37, 35));
SqlParameter suffix = new SqlParameter("#suffix", "test");//retrievedMessage.Substring(72, 10));
SqlParameter corporateName = new SqlParameter("#corporateName", "Initech");//retrievedMessage.Substring(82, 80));
//Insert record into Agent table
sqlConn = (SqlConnection)cm.AcquireConnection(Dts.Transaction);
sqlComm = new SqlCommand
(
"SET IDENTITY_INSERT Agent ON " +
"INSERT INTO Agent (UniqueAgentId, EntityType, FirstName, LastName, NameSuffix, CorporateName, LastChangeOperator, LastChangeDate, ControlId) " +
"VALUES (#agentID, #entityType, #fName, #lName, #suffix, #corporateName, #lastChangeOperator, #lastChangeDate, #controlID)" +
"SET IDENTITY_INSERT Agent OFF",
sqlConn//, sqlTrans
);
sqlTrans = sqlConn.BeginTransaction("SqlAgentTableUpdates");
sqlComm.Parameters.Add(agentID);
sqlComm.Parameters.Add(lastChangeOperator);
sqlComm.Parameters.Add(lastChangeDate);
sqlComm.Parameters.Add(controlId);
sqlComm.Parameters.Add(entityType);
sqlComm.Parameters.Add(fName);
sqlComm.Parameters.Add(lName);
sqlComm.Parameters.Add(suffix);
sqlComm.Parameters.Add(corporateName);
sqlComm.Transaction = sqlTrans;
sqlComm.ExecuteNonQuery();
//Set variables for AgentIdentification table
SqlParameter taxIdType = new SqlParameter("taxIdType", "S");//Hard-coded value for testing - CHANGE LATER
SqlParameter agentTaxId = new SqlParameter("#agentTaxId", "999999999");//Hard-coded value for testing - CHANGE LATER
//Insert record into AgentIdentification table
sqlConn = (SqlConnection)cm.AcquireConnection(Dts.Transaction);
sqlComm = new SqlCommand
(
"INSERT INTO AgentIdentification (UniqueAgentId, TaxIdType, AgentTaxId, LastChangeOperator, LastChangeDate, ControlId) " +
"VALUES (#agentID, #taxIdType, #agentTaxId, #lastChangeOperator, #lastChangeDate, #controlId)",
sqlConn//, sqlTrans
);
sqlComm.Parameters.Add(taxIdType);
sqlComm.Parameters.Add(agentTaxId);
sqlComm.Transaction = sqlTrans;
sqlComm.ExecuteNonQuery();
}
catch (Exception)
{
sqlTrans.Rollback();
cm.ReleaseConnection(sqlConn);
}
finally
{
sqlTrans.Commit();
cm.ReleaseConnection(sqlConn);
}
EDIT
I was able to make this transaction work by eliminating the second connection. However, both queries use a couple of the same variables (SqlParameters). I was forced to duplicate these in order for this to run without errors. Is there a way for them to share the variables so I do not have to waste space re-creating them?
I think the problem might be with the connection, or when you set the command to a new command for the second insert, you can use two different commands with the same connection or reuse one command just changing the CommandText property.
Hope this helps... Using SqlTransaction
A transaction cannot span multiple connections... does cm.AcquireConnection return a new connection each time? If so, try using the same connection for both commands.
use transactionscope
using(TransactionScope ts = new TransactionScope())
{
using(SqlConnection conn = new SqlConnection(myconnstring)
{
conn.Open();
//call first executenonquery
//call second executenonquery
ts.Complete();
conn.Close();
}
}

Categories

Resources