Because your code only performs a single operation - it executes a stored procedure once and once only - it's difficult to really show you what happens with a Transaction.
If instead we do two table updates that depend on each other it should be clearer.
We have two tables:
Table1:
ID Int
Data NVARCHAR(50)
Table2:
ID INT, Identity (assigned by SQL because we don't care what it is, just that it is unique)
Tab1ID INT, Foreign key to ID in Tab1
Data NVARCHAR(50)
The data in Table2 has a foreign key: the ID of the record in Table1.
Think of it as an Invoice (Table1) and the individual items the customer bought (multiple Table2 entries).
If there is no record with that ID in table 1, then the Tabel2 data is invalid. So if the first query fails, we don't want to try and write the second table data. That's easy:
private void TransDemo(int ID, string dataForTab1, string dataForTab2)
{
using (SqlConnection con = new SqlConnection(strConnect))
{
con.Open();
try
{
using (SqlCommand cmd = new SqlCommand("INSERT INTO Table1 (Id, Data) VALUES (@ID, @DT", con))
{
cmd.Parameters.AddWithValue("@ID", ID);
cmd.Parameters.AddWithValue("@DT", dataForTab1);
cmd.ExecuteNonQuery();
}
using (SqlCommand cmd = new SqlCommand("INSERT INTO Table2 (Tab1Id, Data) VALUES (@ID, @DT", con))
{
cmd.Parameters.AddWithValue("@ID", ID);
cmd.Parameters.AddWithValue("@DT", dataForTab2);
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
The
try...catch
block catches the problem, and prevents the second table being written. But what happens if the first insert works, but the second fails? That's also bad - because the first table data has been written, but it isn't "connected" to the second table.
You end up with an invoice for no items - and your company loses money!
This is where a Transaction comes in: You start a transaction before you do any database changes, and if there is a problem you undo the changes (called a "rollback") and throw them all away so your database remains consistent. If it all works, then you approve all the changes (called a "commit") and then and only then is the actual database altered.
private void TransDemo(int ID, string dataForTab1, string dataForTab2)
{
using (SqlConnection con = new SqlConnection(strConnect))
{
con.Open();
SqlTransaction trans = con.BeginTransaction();
try
{
using (SqlCommand cmd = new SqlCommand("INSERT INTO Table1 (Id, Data) VALUES (@ID, @DT", con))
{
cmd.Parameters.AddWithValue("@ID", ID);
cmd.Parameters.AddWithValue("@DT", dataForTab1);
cmd.ExecuteNonQuery();
}
using (SqlCommand cmd = new SqlCommand("INSERT INTO Table2 (Table1Id, Data) VALUES (@ID, @DT", con))
{
cmd.Parameters.AddWithValue("@ID", ID);
cmd.Parameters.AddWithValue("@DT", dataForTab2);
cmd.ExecuteNonQuery();
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
MessageBox.Show(ex.Message);
}
}
}
If you get a problem with either update, the Rollback throws away all the changes, so it doesn't matter where the problem was - it can be in the first or second insert but the database is always consistent and right.