C#: atestat și pregătire ONTI

C#: tranzacții pentru operații care reușesc împreună

Folosește o tranzacție OleDb pentru două actualizări C# legate. Confirmă numai după succesul complet și verifică păstrarea datelor când al doilea pas eșuează.

Unele operații sunt corecte numai dacă toate modificările lor reușesc. Într-un transfer de puncte, scăderea de la primul cont și adăugarea la al doilea formează o singură operație logică. Dacă al doilea pas eșuează, primul nu trebuie păstrat. O tranzacție grupează modificările până la Commit sau Rollback.

Cum lucrezi cu exemplul

Exemplul se compilează ca aplicație de consolă .NET Framework 4.8. Rulează pe Windows, cu provider ACE compatibil cu arhitectura procesului și o copie de test Data/catalog.accdb. Arhiva conține codul și pașii de pregătire, nu un fișier Access preconstruit.

Descarcă sursele și instrucțiunile.

Ideea și pașii de implementare

În copia Access de test creează Accounts cu AccountId Number/Long Integer cheie primară și Balance Number/Long Integer. Adaugă (1,100) și (2,40). Comanda de debitare verifică în WHERE și fondurile disponibile. Toate comenzile folosesc aceeași conexiune și aceeași tranzacție.

Fiecare ExecuteNonQuery trebuie să afecteze exact un rând. Confirmăm numai după ambele verificări. La eroare, încercăm rollback și propagăm eroarea originală. Valoarea transferată este un întreg pozitiv; pentru bani reali ar trebui proiectate explicit unitatea, precizia și toate regulile financiare.

Exemplu și rezultat așteptat

Transferul a 30 de puncte produce soldurile 70 și 70, păstrând totalul 140. Dacă ID-ul destinației devine 99, creditarea afectează zero rânduri și tranzacția este anulată. Soldurile trebuie să rămână 100 și 40, dacă pornești din setul inițial.

Schemă pentru lecția 38: Solduri: 100 și 40; Debit 30; credit 30; Ambele afectează 1 rând; Commit → 70 și 70.Solduri: 100 și 40 → Debit 30; credit 30 → Ambele afectează 1 rând → Commit → 70 și 70.Mărește schema ↗

Codul complet

using System;
using System.Data;
using System.Data.OleDb;
using System.IO;
class Program {
    static string ConnectionString() {
        string path=Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"Data","catalog.accdb");
        if(!File.Exists(path))throw new FileNotFoundException("Pregateste baza de test Data/catalog.accdb.");
        var builder=new OleDbConnectionStringBuilder();
        builder.Provider="Microsoft.ACE.OLEDB.12.0";builder.DataSource=path;
        return builder.ConnectionString;
    }
    static void Main() {
        int amount=30,from=1,to=2;
        if(amount<=0 || from==to)throw new ArgumentException("Transfer invalid");
        using(var connection=new OleDbConnection(ConnectionString())) {
            connection.Open();using(var tx=connection.BeginTransaction()) {
                try {
                    using(var debit=new OleDbCommand("UPDATE Accounts SET Balance = Balance - ? WHERE AccountId = ? AND Balance >= ?",connection,tx)) {
                        debit.Parameters.Add("amount",OleDbType.Integer).Value=amount;
                        debit.Parameters.Add("from",OleDbType.Integer).Value=from;
                        debit.Parameters.Add("minimum",OleDbType.Integer).Value=amount;
                        if(debit.ExecuteNonQuery()!=1)throw new InvalidOperationException("Debit refuzat");
                    }
                    using(var credit=new OleDbCommand("UPDATE Accounts SET Balance = Balance + ? WHERE AccountId = ?",connection,tx)) {
                        credit.Parameters.Add("amount",OleDbType.Integer).Value=amount;
                        credit.Parameters.Add("to",OleDbType.Integer).Value=to;
                        if(credit.ExecuteNonQuery()!=1)throw new InvalidOperationException("Destinatie inexistenta");
                    }
                    tx.Commit();Console.WriteLine("Transfer confirmat");
                } catch {
                    try {tx.Rollback();} catch(OleDbException) { } catch(InvalidOperationException) { }
                    throw;
                }
            }
        }
    }
}

Greșeli și limite

Nu crea a doua conexiune pentru al doilea pas și nu uita să asociezi comanda tranzacției. Nu afișa succes după debitare. Nu ignora zero rânduri afectate. Testul cel mai important provoacă intenționat eșecul după primul pas și verifică faptul că nu există modificare parțială.

Exersează

Rulează succesul pe o copie proaspătă, apoi scenariul cu destinație inexistentă pe altă copie proaspătă. Verifică prin SELECT ambele solduri, nu doar mesajul consolei. Încearcă transferul de 130 de puncte și confirmă refuzul fără sold negativ.

Continuă parcursul

Parcursul complet de 50 de lecții

Bazele C#

Lecția precedentă: C#: BindingSource și filtrarea unui DataTable

Lecția următoare: C#: parole, salt și verificare într-un proiect didactic.

Referință: documentația Microsoft.

Întrebări frecvente

Când apelez Commit?

După ce toate comenzile și verificările operației au reușit.

Ce se întâmplă dacă al doilea pas eșuează?

Tranzacția trebuie anulată, astfel încât primul pas să nu rămână aplicat singur.

Este suficientă aceeași conexiune?

Nu. Comenzile trebuie asociate și aceleiași tranzacții active.

Un pas mai departe

Autentifică-te pentru a păstra progresul în cont.

Exersează cu o problemă →