Grafikus C# – Windows Forms tanfolyam › 13.6 lecke

Windows Forms · 13.6 lecke · környezeti hibák

Fájlkezelési és adatbázis-kivételek

A felhasználó hibás bevitele ellen a program ellenőrzéssel védekezik. De van egy hibacsoport, amelyet a legszigorúbb ellenőrzés sem zár ki: a környezet hibái. A fájl a program indítása és megnyitása között eltűnhet; a pendrive kiesik; a másik program zárolja a fájlt; a lemez megtelik; az adatbázis foglalt vagy sérült. Ezekre a program csak kivételkezeléssel készülhet fel. Ebben a leckében megtanulod a leggyakoribb fájl- és mappakivételeket, a fájlzár és az újrapróbálás kezelését, azt, miért nem elég a File.Exists, a biztonságos mentést, és az adatbázis-hibák (SQLite) kezelését: paraméteres lekérdezés, tranzakció és visszagörgetés.

  • kb. 110 perc
  • haladó szint
  • fájl és adatbázis
Diáklány C# programot készít laptopon

A lecke végére

  • felismered a fájl- és mappakivételeket (FileNotFoundException, UnauthorizedAccessException, IOException...), és érthető üzenetet adsz;
  • érted, miért nem elég a File.Exists (versenyhelyzet), és mikor kell mégis try–catch;
  • kezeled a fájlzárat újrapróbálással;
  • biztonságosan mentesz (ideiglenes fájl, csere), és using blokkal zársz;
  • adatbázis-hibákat kezelsz (kapcsolat, DbException), paraméteres lekérdezést használsz;
  • tranzakciót indítasz, és hiba esetén visszagörgetsz.

Mielőtt elkezded

Ehhez a leckéhez a 10. fejezet (fájlkezelés), a 13.4 lecke (try–catch–finally) és az 1–3. fejezet SQL alapjai szükségesek. Az adatbázis-részhez a Microsoft.Data.Sqlite NuGet csomag kell.

13.5 Bevitelek →

A fájl- és mappakivételek

KivételMikor?Felhasználói üzenet / teendő
FileNotFoundExceptionA fájl nem létezik.„A fájl nem található.” Ajánlj másik fájlt / új létrehozását.
DirectoryNotFoundExceptionA mappa (útvonal egy része) nem létezik (pl. kivett pendrive).„A mappa nem érhető el.” Ellenőrizd a meghajtót.
UnauthorizedAccessExceptionNincs jogosultság, a fájl csak olvasható, vagy a megadott útvonal mappa.„Nincs jogosultság.” Másik mentési hely.
IOException (általános)Fájlzár (másik folyamat használja), megtelt lemez, megszakadt kapcsolat, hálózati hiba.A HResult és az üzenet alapján: „Zárd be a másik programot”, „A lemez megtelt”.
PathTooLongExceptionAz útvonal túl hosszú (klasszikusan 260 karakter).„Túl hosszú útvonal.” Rövidebb név/mappa.
ArgumentException, NotSupportedExceptionÉrvénytelen karakter az útvonalban, vagy hibás útvonalformátum.A bevitelt ellenőrizd előre (13.5 lecke).
SecurityExceptionBiztonsági korlát (ritka asztali programban).Jogosultság kérése.
FajlSegedek.csC#
// az általános kivételből ember által olvasható üzenet; a Formnak nem kell ismernie a típusokat
public static string Magyarazat(Exception ex)
{
if (ex is FileNotFoundException) { return "A fájl nem található."; }
if (ex is DirectoryNotFoundException) { return "A mappa nem érhető el (kivették a meghajtót?)."; }
if (ex is UnauthorizedAccessException) { return "Nincs jogosultság a művelethez (a fájl csak olvasható, vagy védett mappa)."; }
if (ex is PathTooLongException) { return "Az útvonal túl hosszú."; }

IOException io = ex as IOException;

if (io != null)
{
int kod = io.HResult & 0xFFFF; // a Win32 hibakód alsó 16 bitje

if (kod == 32 || kod == 33) { return "A fájlt másik program használja. Zárd be, és próbáld újra."; }
if (kod == 39 || kod == 112) { return "A lemez megtelt."; }

return "Fájlművelet-hiba: " + io.Message;
}

return "Váratlan hiba: " + ex.Message;
}
Leszármazási sorrendA FileNotFoundException és a DirectoryNotFoundException az IOException leszármazottja: a catchben előbb a speciálisak, utána az IOException (13.4 lecke). Az UnauthorizedAccessException nem az IOException leszármazottja: külön ág kell.
A HResultAz IOException.HResult a Windows hibakódot hordozza: 32 (sharing violation: másik folyamat használja), 33 (lock violation), 39 és 112 (lemez megtelt). Ez finomítja az általános IOException üzenetét. A kódok Windowsra jellemzőek; más rendszeren eltérnek.
Magyarázó segédEgy központi Magyarazat(ex) metódus a hibaüzenetek egy helyen élnek: a Form, a napló és a teszt ugyanazt használja, és a hibaszövegek egységesek.
Hálózati meghajtóA hálózati és cserélhető meghajtón a hibák gyakoribbak és lassabban jönnek (időtúllépés): a fájlművelet blokkolhat is (11.4 lecke): hosszú műveletet Task.Run alatt, megszakíthatóan érdemes végezni.

Jósolj!

A mentés útvonala @"C:\Windows\System32\jegyzet.txt" egy normál (nem rendszergazda) felhasználónál. Melyik kivételt kapod?

Jóslat ellenőrzése

UnauthorizedAccessException: védett mappába normál jogosultsággal nem írhat a program. A felhasználói adatok helye az Environment.SpecialFolder.ApplicationData vagy a Dokumentumok mappa (12.8 lecke).

File.Exists és a try–catch: miért kell mindkettő?

FajlSegedek.csC#
// ELÉG? NEM: az ellenőrzés és a használat KÖZÖTT a fájl eltűnhet (versenyhelyzet: TOCTOU)
if (File.Exists(ut))
{
// ... közben egy másik program törli / zárolja / a hálózat megszakad ...
string s = File.ReadAllText(ut); // FileNotFoundException még mindig lehet!
}

// HELYES: a try–catch az igazság; az Exists csak kényelmi előszűrő (jobb üzenet)
public static string OlvasBiztonsagosan(string ut, out string hiba)
{
hiba = null;

try { return File.ReadAllText(ut); }
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException || ex is NotSupportedException)
{
hiba = Magyarazat(ex);

return null;
}
}

// használat
string hibaUzenet;
string tartalom = FajlSegedek.OlvasBiztonsagosan(txtUt.Text, out hibaUzenet);

if (tartalom == null) { tsslUzenet.Text = hibaUzenet; return; }
TOCTOUA „time of check, time of use” hiba: az ellenőrzés (Exists) és a használat (ReadAllText) között az állapot megváltozhat. A File.Exists tehát nem garancia, csak előszűrés (például az „itt a fájl” üzenethez).
Az igazság: a kivételA műveletet mindig védd try–catch-csel, és az Exists-szel legfeljebb a felhasználói üzenetet finomítod. A fordított mintát (csak Exists, nincs try) kerüld.
Szűk catchA when szűrő csak a várható környezeti kivételeket kapja el; a programozói hibák (NullReferenceException) áthaladnak és kiderülnek (13.4 lecke).
Out paraméter vagy kivétel?A bool Try...(out ...) minta kényelmes, ha a hívó a hibát megjeleníti, és tovább lép. Ha a hívónak nincs mit tennie, hagyd a kivételt felszállni.

Jósolj!

A program if (File.Exists(ut)) { tartalom = File.ReadAllText(ut); } kódot használ try nélkül, és a felhasználó közben kihúzza a pendrive-ot. Mi történik?

Jóslat ellenőrzése

Az Exists még igazat adott, de az ReadAllText kivételt dob (DirectoryNotFoundException vagy IOException), és a program kezeletlen hibával áll le (vagy a globális kezelőbe kerül). Az Exists nem véd az utólagos változástól.

Fájlzár, újrapróbálás és biztonságos mentés

FajlSegedek.cs (using System.Threading;)C#
// ÚJRAPRÓBÁLÁS: a fájlzár gyakran átmeneti (víruskereső, felhő-szinkron, másik program)
public static void MentUjraprobalva(string ut, string tartalom, int probak = 4)
{
for (int proba = 1; ; proba++)
{
try
{
MentBiztonsagosan(ut, tartalom);

return;
}
catch (IOException ex) when (proba < probak && ZarHiba(ex)) // csak a zárhibánál, és csak ennyiszer
{
Thread.Sleep(150 * proba); // növekvő várakozás (háttérszálon; felületi szálon: await Task.Delay, 11.4 lecke)
}
// az utolsó próbánál (vagy más hibánál) a kivétel továbbszáll
}
}

private static bool ZarHiba(IOException ex) { int k = ex.HResult & 0xFFFF; return k == 32 || k == 33; }

// BIZTONSÁGOS MENTÉS (10.9 lecke): ideiglenes fájl, majd csere; a régi tartalom nem sérül, ha a mentés megszakad
public static void MentBiztonsagosan(string ut, string tartalom)
{
string tmp = ut + ".tmp";

try
{
File.WriteAllText(tmp, tartalom, new UTF8Encoding(false));

if (File.Exists(ut)) { File.Replace(tmp, ut, ut + ".bak"); } // atomi csere, biztonsági másolattal
else { File.Move(tmp, ut); }
}
catch
{
try { if (File.Exists(tmp)) { File.Delete(tmp); } } catch { } // takarítás; a takarítás hibája ne fedje el az eredetit

throw; // az eredeti kivétel megy tovább
}
}

// ERŐFORRÁS: using: a fájl mindig lezárul (try–finally)
using (StreamReader r = new StreamReader(ut)) { string sor = r.ReadLine(); }
Mikor újrapróbálj?Csak átmeneti hibánál (fájlzár, hálózati időtúllépés), korlátozott számban, növekvő várakozással. A tartós hibát (nincs jogosultság, nincs ilyen mappa) nem érdemes újrapróbálni: haszontalanul késleltet.
Felületi szálon?A Thread.Sleep a felületi szálon lefagyasztja az ablakot (11.4 lecke). Hosszabb műveletet háttérben végezz (await Task.Run), és a várakozás await Task.Delay legyen.
File.ReplaceA File.Replace(forras, cel, mentes) atomi (Windowson, azonos meghajtón): vagy az új, vagy a régi tartalom marad, nem a kettő keveréke. Hálózati meghajtón korlátai lehetnek (13.7 és a gépen teszteld).
usingA using a stream-et a hiba esetén is lezárja (finally): nyitva maradt fájl az egyik leggyakoribb zár oka. Minden IDisposable-t (StreamReader, FileStream, kapcsolat) using-ba tegyél.

Jósolj!

A mentés nem ideiglenes fájlba, hanem közvetlenül írja a jegyzet.txt fájlt (File.WriteAllText). A program a mentés közepén összeomlik (áramszünet). Mi lesz a fájl tartalma?

Jóslat ellenőrzése

A fájlt a WriteAllText az írás elején csonkolja: a megszakadás után csonka vagy üres fájl marad, a régi tartalom elveszett. Az ideiglenes fájl + csere mintával a régi változat sértetlen marad.

Adatbázis-kivételek: SQLite, paraméterek, tranzakció

JegyzetAdatbazis.cs (NuGet: Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;)C#
public class AdatbazisHiba : Exception { public AdatbazisHiba(string uzenet, Exception ok) : base(uzenet, ok) { } }

public class JegyzetAdatbazis
{
private readonly string kapcsolat;

public JegyzetAdatbazis(string fajl) { kapcsolat = "Data Source=" + fajl; }

public void Letrehoz()
{
Vegrehajt("CREATE TABLE IF NOT EXISTS jegyzet (id INTEGER PRIMARY KEY AUTOINCREMENT, cim TEXT NOT NULL UNIQUE, szoveg TEXT)");
}

public void Hozzaad(string cim, string szoveg)
{
// PARAMÉTERES lekérdezés: SOHA ne fűzd össze a bemenetet az SQL-lel (SQL-injekció, aposztróf hiba)
Vegrehajt("INSERT INTO jegyzet (cim, szoveg) VALUES ($cim, $szoveg)", ("$cim", cim), ("$szoveg", szoveg));
}

private void Vegrehajt(string sql, params (string nev, object ertek)[] parameterek)
{
try
{
using (SqliteConnection kap = new SqliteConnection(kapcsolat)) // using: a kapcsolat mindig lezárul
using (SqliteCommand parancs = kap.CreateCommand())
{
kap.Open();
parancs.CommandText = sql;

foreach (var p in parameterek) { parancs.Parameters.AddWithValue(p.nev, p.ertek ?? DBNull.Value); }

parancs.ExecuteNonQuery();
}
}
catch (SqliteException ex) when (ex.SqliteErrorCode == 19) // 19: megszorítás megsértése (UNIQUE, NOT NULL)
{
throw new AdatbazisHiba("Ilyen című jegyzet már létezik, vagy hiányzik egy kötelező adat.", ex);
}
catch (SqliteException ex) when (ex.SqliteErrorCode == 5 || ex.SqliteErrorCode == 6) // 5: SQLITE_BUSY, 6: LOCKED
{
throw new AdatbazisHiba("Az adatbázis foglalt, próbáld újra később.", ex);
}
catch (SqliteException ex)
{
throw new AdatbazisHiba("Adatbázis-hiba: " + ex.Message, ex);
}
}

// TRANZAKCIÓ: több művelet együtt: vagy mind sikerül, vagy egyik sem
public void Importal(IEnumerable<(string cim, string szoveg)> jegyzetek)
{
try
{
using (SqliteConnection kap = new SqliteConnection(kapcsolat))
{
kap.Open();

using (SqliteTransaction tr = kap.BeginTransaction())
{
try
{
foreach (var j in jegyzetek)
{
using (SqliteCommand p = kap.CreateCommand())
{
p.Transaction = tr;
p.CommandText = "INSERT INTO jegyzet (cim, szoveg) VALUES ($c, $s)";
p.Parameters.AddWithValue("$c", j.cim);
p.Parameters.AddWithValue("$s", j.szoveg);
p.ExecuteNonQuery();
}
}

tr.Commit(); // csak ha minden sikerült
}
catch
{
tr.Rollback(); // bármi hiba: mindent visszavon
throw;
}
}
}
}
catch (SqliteException ex) { throw new AdatbazisHiba("Az importálás nem sikerült, egyetlen jegyzet sem került be: " + ex.Message, ex); }
}
}
A kapcsolat is erőforrásAz adatbázis-kapcsolat és -parancs IDisposable: using nélkül nyitva maradnak, és az adatbázis zárolt lehet. Rövid életű kapcsolat (használatkor nyit, utána zár): a kapcsolatkészlet (connection pool) gyorssá teszi.
Paraméteres lekérdezésA $cim paraméter az értéket külön adja át, nem az SQL szövegébe. Ez véd az SQL-injekció ellen ("'; DROP TABLE jegyzet; --" mint cím), és az aposztrófos szöveget (O'Brien) is helyesen kezeli. Soha ne használj "... VALUES ('" + cim + "')" összefűzést.
HibakódokA SqliteException.SqliteErrorCode megmondja a hiba fajtáját: 19 (megszorítás: UNIQUE/NOT NULL), 5 (busy: foglalt), 6 (locked), 1 (általános), 11 (sérült adatbázis). A hibakódokat a saját típusodra fordítod (AdatbazisHiba), hogy a Form ne ismerje az adatbázis-motort (12.8 lecke).
TranzakcióTöbb összetartozó művelet (import, átutalás) egy tranzakcióban fut: a Commit mindet érvényesíti, a Rollback mindet visszavonja. Hibánál a félkész állapot nem marad az adatbázisban. A using a tranzakciót is felszabadítja; ha nincs Commit, automatikusan visszagörgetődik.

Jósolj!

Egy 100 jegyzetes importban a 60. jegyzet címe már létezik (UNIQUE megszorítás). Tranzakció nélkül, soronkénti beszúrással hány jegyzet marad az adatbázisban a hiba után? És tranzakcióval?

Jóslat ellenőrzése

Tranzakció nélkül 59 új jegyzet marad (félig kész állapot: nem tudod, melyik hiányzik). Tranzakcióval 0: a Rollback az egészet visszavonja, az adatbázis a hiba előtti állapotban van, és újra próbálható.

📷 Képernyőkép helye

Mit tegyél ide: a biztonságos adatkezelő: kezelt fájl- és adatbázis-hibák üzenetei és a naplója.Javasolt fájlnév: winforms-13-6-adatkezelo.webp · Alt szöveg: A biztonságos adatkezelő program

Hibakeresés: te vagy a hibadetektív

Mi a baj, és hogyan javítod?

Gondold végig az okot, és csak utána nyisd le a megoldást.

1. A fájl létezik, mégis kivétel

if (File.Exists(ut)) { string s = File.ReadAllText(ut); }
Hiba és javítás

Az Exists nem garancia (versenyhelyzet). Védd try–catch-csel; az Exists csak előszűrés.

2. A fájl zárolva marad

StreamReader r = new StreamReader(ut);
string s = r.ReadToEnd();
// nincs Close / using
Hiba és javítás

A nyitott stream zárolja a fájlt (a következő művelet IOException). using blokk, amely hiba esetén is lezár.

3. SQL-injekció

parancs.CommandText = "INSERT INTO jegyzet (cim) VALUES ('" + txtCim.Text + "')";
Hiba és javítás

Egy aposztróf a szövegben elrontja az SQL-t, és a rosszindulatú bemenet futtat is parancsot. Paraméteres lekérdezés ($cim, Parameters.AddWithValue).

4. Félig kész import

Az importálás közepén hiba van, és az adatbázisban csak a bemenet egy része maradt.

Ok és javítás

Nincs tranzakció. BeginTransaction, hibánál Rollback, siker esetén Commit.

5. Az újrapróbálás mindig lefut

catch (IOException) { Thread.Sleep(1000); Mentes(); }   // minden IOException-nél, korlát nélkül
Hiba és javítás

Tartós hibánál (jogosultság, teli lemez) végtelen várakozás. Csak átmeneti hibánál (zár: 32/33), korlátozott számú próbálkozással.

6. A felület lefagy a mentéskor

Hálózati meghajtóra mentéskor az ablak másodpercekig nem reagál.

Ok és javítás

A fájlművelet a felületi szálon blokkol (11.4 lecke). Háttérbe (await Task.Run), a gomb tiltva a mentés idejére, és a hibakezelés a try–catch–finally alatt.

Mini projekt: biztonságos adatkezelő

Készíts jegyzetkezelőt, amely fájlba és SQLite adatbázisba is ment, és minden környezeti hibát érthetően kezel:

  1. Felület: txtCim, txtSzoveg, txtUt, Fájlba ment, Fájlból betölt, Adatbázisba ment, Import (tranzakció); lstJegyzetek; lstNaplo; állapotsor.
  2. FajlSegedek: Magyarazat(ex), OlvasBiztonsagosan, MentBiztonsagosan, MentUjraprobalva (a leckéből); a Formban nincs fájltípusú kivételkezelés.
  3. JegyzetAdatbazis: Letrehoz, Hozzaad (paraméteres), Mind() (olvasás SqliteDataReader-rel, using alatt), Importal (tranzakció), AdatbazisHiba.
  4. Hibakísérletek: egy Hibaszimulátor menü: (a) nem létező fájl, (b) védett mappa (C:\Windows\System32), (c) zárolt fájl (egy másik programmal, pl. Jegyzettömb vagy Excel megnyitva), (d) érvénytelen útvonal (a|b), (e) azonos című jegyzet kétszer, (f) import közepén duplikátum: tranzakció visszagörgetése.
  5. Újrapróbálás: a fájlmentés zárhibánál 4-szer próbálkozik (háttérszálon, Task.Delay); a felület közben él, a napló mutatja a próbálkozásokat.
  6. Aszinkron: minden fájl- és adatbázis-művelet await Task.Run(...) alatt, a gombok finally-ban visszaállnak, a kurzor is.
  7. Hibatábla: a leírásban táblázat: kísérlet, várt kivétel, kapott üzenet, mi történt az adattal (megmaradt a régi? félig kész?).
  8. Biztonság: egy SQL-injekciós szöveggel (x'); DROP TABLE jegyzet; --) próbáld ki: a paraméteres lekérdezés ártalmatlan, az összefűzéses változat (csak a leírásban, a teszthez) elromlik.
  9. Teszteld: minden hibakísérlet, a tranzakció visszagörgetése (az adatbázisban nem marad félig kész import), a mentés megszakítása (az ideiglenes fájl takarítása), a Form bezárása mentés közben.
Megoldás megnyitása (próbáld ki előbb önállóan!)
Form1.cs részletek (using System.IO; using System.Threading.Tasks; using Microsoft.Data.Sqlite;)C#
private readonly JegyzetAdatbazis db;
private readonly List<string> naplo = new List<string>();

public Form1()
{
InitializeComponent();

string mappa = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jegyzetek");

Directory.CreateDirectory(mappa);
db = new JegyzetAdatbazis(Path.Combine(mappa, "jegyzetek.db"));
}

private void Naploz(string s)
{
naplo.Insert(0, DateTime.Now.ToString("HH:mm:ss") + " " + s);
lstNaplo.Items.Clear();
lstNaplo.Items.AddRange(naplo.Take(60).ToArray());
}

private async void btnFajlbaMent_Click(object sender, EventArgs e)
{
string ut = txtUt.Text.Trim(), tartalom = txtCim.Text + Environment.NewLine + txtSzoveg.Text;

Muvelet(false);

try
{
await Task.Run(() => FajlSegedek.MentUjraprobalva(ut, tartalom));
Naploz("Fájlba mentve: " + ut);
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException || ex is NotSupportedException)
{
string uzenet = FajlSegedek.Magyarazat(ex);

tsslUzenet.Text = uzenet;
Naploz("Mentési hiba: " + uzenet + " [" + ex.GetType().Name + "]");
}
finally { Muvelet(true); }
}

private void btnFajlbolBetolt_Click(object sender, EventArgs e)
{
string hiba;
string tartalom = FajlSegedek.OlvasBiztonsagosan(txtUt.Text.Trim(), out hiba);

if (tartalom == null) { tsslUzenet.Text = hiba; Naploz("Olvasási hiba: " + hiba); return; }

txtSzoveg.Text = tartalom;
}

private async void btnAdatbazisba_Click(object sender, EventArgs e)
{
string cim = txtCim.Text, szoveg = txtSzoveg.Text;

Muvelet(false);

try
{
await Task.Run(() => { db.Letrehoz(); db.Hozzaad(cim, szoveg); });
Naploz("Adatbázisba mentve: " + cim);
}
catch (AdatbazisHiba ex) { tsslUzenet.Text = ex.Message; Naploz("Adatbázis-hiba: " + ex.Message + " [" + ex.InnerException.GetType().Name + "]"); }
finally { Muvelet(true); }
}

private async void btnImport_Click(object sender, EventArgs e)
{
// szándékos duplikátum az 5. elemnél: a tranzakciót mutatja
List<(string, string)> adat = new List<(string, string)>();

for (int i = 1; i <= 10; i++) { adat.Add(("Import " + (i == 5 ? 1 : i), "szöveg " + i)); }

Muvelet(false);

try
{
await Task.Run(() => { db.Letrehoz(); db.Importal(adat); });
Naploz("Import kész: 10 jegyzet.");
}
catch (AdatbazisHiba ex) { tsslUzenet.Text = ex.Message; Naploz("Import visszagörgetve: " + ex.Message); }
finally { Muvelet(true); }
}

private void Muvelet(bool kesz)
{
btnFajlbaMent.Enabled = btnFajlbolBetolt.Enabled = btnAdatbazisba.Enabled = btnImport.Enabled = kesz;
Cursor = kesz ? Cursors.Default : Cursors.WaitCursor;
}
📷 Képernyőkép helye

Mit tegyél ide: a kész adatkezelő a hibakísérletek naplójával.Javasolt fájlnév: winforms-13-6-kesz.webp · Alt szöveg: A kész biztonságos adatkezelő

Három önálló gyakorlófeladat

Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.

1. Hálózati meghajtó

Hogyan kezeled, ha a mentési cél hálózati meghajtó, amely időnként elérhetetlen?

Egy lehetséges megoldás

Háttérszálon mentés, időtúllépéssel (CancellationToken, 11.8 lecke), újrapróbálás átmeneti hibánál, majd tartalék hely (helyi mappa) felajánlása és a felhasználó értesítése.

2. Adatbázis-lekérdezés paraméterrel

Írj keresést cím szerint (LIKE), paraméterrel.

Egy lehetséges megoldás

SELECT * FROM jegyzet WHERE cim LIKE $k, a paraméter "%" + keres + "%" (a % a paraméter értékében van, nem az SQL szövegben). Az eredményt SqliteDataReaderrel olvasod using alatt.

3. Teli lemez szimulálása

Hogyan próbálnád ki a „lemez megtelt” (112) hibakezelést?

Egy lehetséges megoldás

Kis méretű virtuális lemez (VHD) vagy kvótás mappa; vagy a hibakezelőt egységteszttel próbálod, egy hamis IOException-nel, amelynek a HResult értéke 112 (a Magyarazat tiszta függvény).

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Melyik kivétel?

Védett mappába írsz; a mappa nem létezik; a fájl nem létezik. Melyik kivétel?

Megoldás megnyitása

UnauthorizedAccessException; DirectoryNotFoundException; FileNotFoundException.

2. using

Mire jó a using fájl és adatbázis esetén?

Megoldás megnyitása

Hiba esetén is lezárja az erőforrást (finally): nem marad zárolt fájl vagy nyitott kapcsolat.

Közepes szint

3. Exists

Miért nem elég a File.Exists?

Megoldás megnyitása

Az ellenőrzés és a használat között a fájl eltűnhet vagy zárolódhat (versenyhelyzet); a try–catch kell.

4. Paraméter

Miért paraméteres a lekérdezés?

Megoldás megnyitása

Véd az SQL-injekció ellen, és helyesen kezeli az aposztrófot és a speciális karaktereket.

Kihívás szint

5. Tranzakció

Mit biztosít a tranzakció?

Megoldás megnyitása

Atomitást: az összes művelet együtt érvényesül (Commit), vagy egyik sem (Rollback): nincs félig kész állapot.

6. Mikor ne próbáld újra?

Melyik hibát nem érdemes újrapróbálni?

Megoldás megnyitása

A tartós hibát: jogosultság hiánya, nem létező mappa, teli lemez, megszegett megszorítás. Csak az átmeneti hibát (fájlzár, foglalt adatbázis).

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Mi a környezeti hiba?

A programon kívüli ok miatti hiba (fájl, lemez, jogosultság, adatbázis, hálózat), amely ellenőrzéssel nem zárható ki.

2. Mi a TOCTOU?

Az ellenőrzés és a használat közötti időben megváltozó állapot miatti hiba.

3. Mikor ismételsz?

Átmeneti hibánál (zár, foglalt), korlátozott számban, növekvő várakozással.

4. Hogyan mentesz biztonságosan?

Ideiglenes fájlba írsz, majd cserélsz (File.Replace/Move).

5. Mit tesz a Rollback?

Visszavonja a tranzakció összes változását.

6. Mi a paraméteres lekérdezés előnye?

SQL-injekció elleni védelem, helyes speciális karakter-kezelés.

7. Miért saját hibatípus (AdatbazisHiba)?

A Form ne ismerje az adatbázis-motort; egyetlen típust kezel.

Mit tudsz már?

  • Kezeled a fájl- és mappakivételeket, és érthető üzenetet adsz.
  • Érted a versenyhelyzetet, és biztonságosan mentesz újrapróbálással.
  • Adatbázis-hibákat kezelsz, paraméteres lekérdezést és tranzakciót használsz.
  • A fájlokat és kapcsolatokat using blokkal zárod.

Következő lecke: Diagnosztika és naplózás

A program saját „fekete doboza”: naplózás szintekkel, naplófájl, Debug és Trace, és hogyan olvasol naplót hibakereséskor.