C# - Tranzakció-kezelés a .NET-ben

forráskód letöltése
Ha egy adatbázisban úgy tárolunk adatot, hogy egy művelet elvégzésekor több tábla adata is módosul, kiegészül új sorral, akkor célszerű kihasználnunk a tranzakció-kezelés nyújtotta előnyöket, hogy adataink minden esetben úgy kerüljenek az adatbázisba, ahogy azt elképzeltük a tervezés idején. Ha ugyanis hiba történne az adatbevitel közben, akkor nem kívánatos hatásokat hozhat az, ha mondjuk az egyik táblában már megtörtént a változás, de egy másikban még nem. Ekkor a hiba esetén egyetlen jó megoldás, hogy mindenképpen helyreállítsuk az eredeti állapotot, ha már az adattárolást nem is tudjuk véghezvinni.
A példa futásához szükséges a TransactTest adatbázis létrehozása, melyet a mellékelt TransactTest.sql futtatásával tehet meg.
A példa egyszerűségének kedvéért az adattárolás most csak egy táblába történik meg, de oly módon, hogy több új sort is elhelyezünk benne több úton is. Létrehozunk új sort tárolt eljárás lévén és SqlCommand osztály felhasználásával is.
Ha hiba történik - márpedig fog, mert erről gondoskodunk -, akkor a tranzakció-kezelés biztosítja, hogy helyreálljon az eredeti állapot és a már felvett új sorok eltűnjenek.
    private void button1_Click(object sender, System.EventArgs e)
    {
      SqlConnection connection = new SqlConnection(connStr);
      connection.Open();
Új tranzakciót kell nyitnunk mielőtt még bármilyen módosító tevékenységet végeznénk. Ehhez az SqlConnection osztály BeginTransaction függvényét hívjuk meg. Visszatérési értékként egy SqlTransaction osztályt kapunk. Ezt tároljuk a későbbi felhasználás érdekében.
      SqlTransaction transaction = connection.BeginTransaction();
Ezt követően fontos, hogy a használandó SqlCommand osztály Transaction property-jének értékül adjuk az imént kapott SqlTransaction példányát.
      SqlCommand c = new SqlCommand();
      c.Connection = connection;
      c.Transaction = transaction;
Fontos az is, hogy a műveleteinket try - catch blokk között végezzük, hogy ha hiba történne, akkor ennek megfelelően tudjunk cselekedni.
      try
      {     
Hozzunk létre tehát egy új adatsort az InsertNewRow tárolt eljárás használatával.
        c.CommandText = "InsertNewRow";
        c.CommandType = CommandType.StoredProcedure;
        c.Parameters.Add("@newValue", SqlDbType.Int).Value = 0;
        c.ExecuteNonQuery();
        c.Parameters.Clear();
Ezt követően egy újabb sort hozunk létre egy programból megfogalmazott insert into utasítással.
        c.CommandText = "insert into Table1 (RowValue) VALUES (1)";
        c.CommandType = CommandType.Text;
        c.ExecuteNonQuery();
Mielőtt a harmadik új sort is létrehoznánk vizsgáljuk, hogy a CheckBox1 kiválasztott-e. Ha igen, akkor létrehozunk egy futási hibát, melyhez a nullával való osztás a garancia.
        if (checkBox1.Checked)
        {
          int a = 0;
          int b = 0;
          Text = (a / b).ToString();
        }
Ha hiba történik, akkor ez a rész már nem fut, így a harmadik új sor nem jöhet létre.
        c.CommandText = "InsertNewRow";
        c.CommandType = CommandType.StoredProcedure;
        c.Parameters.Add("@newValue", SqlDbType.Int).Value = 2;
        label1.Text = c.ExecuteScalar().ToString();
Ha nincs hiba, akkor zárnunk kell a tranzakciót az SqlTransaction osztály Commit függvényének hívásával.
        transaction.Commit();
Végül futtatunk egy lekérdezést annak érdekében, hogy láthassuk az új sorok felvételét a táblába.
        SqlDataAdapter da = new SqlDataAdapter("select * from Table1", connection);
        DataSet ds = new DataSet();
        da.Fill(ds);
        dataGrid1.DataSource = ds.Tables[0];
      }
Ha hiba történik, akkor a catch blokkban folytatódik a programunk futása, ahol az SqlTransaction osztály Rollback függvényének hívásával visszavonhatjuk a BeginTransaction hívása óta történt változtatásokat.
      catch
      {
        transaction.Rollback();
      }
Fontos, hogy használjuk a finally blokkot is, ahol az SQL kapcsolatot zárhatjuk. Erre a lépésre mindig szükségünk van, akár történik hiba, akár nem.
      finally
      {
        connection.Close();
      }
    }