Grafikus C# – Windows Forms tanfolyam › 13.6 lecke Windows Forms · 13.6 lecke · környezeti hibák 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 Fájlkezelési és adatbázis-kivételek
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.
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égistry–catch; - kezeled a fájlzárat újrapróbálással;
- biztonságosan mentesz (ideiglenes fájl, csere), és
usingblokkal 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.
A fájl- és mappakivételek
| Kivétel | Mikor? | Felhasználói üzenet / teendő |
|---|---|---|
FileNotFoundException | A fájl nem létezik. | „A fájl nem található.” Ajánlj másik fájlt / új létrehozását. |
DirectoryNotFoundException | A mappa (útvonal egy része) nem létezik (pl. kivett pendrive). | „A mappa nem érhető el.” Ellenőrizd a meghajtót. |
UnauthorizedAccessException | Nincs 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”. |
PathTooLongException | Az ú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). |
SecurityException | Biztonsági korlát (ritka asztali programban). | Jogosultság kérése. |
// 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;
}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.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.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.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ő?
// 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; }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).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.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).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
// Ú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(); }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.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).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ó
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); }
}
}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.$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.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).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ó.
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 / usingHiba é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. Nincs tranzakció. Ok és javítás
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ülHiba é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. A fájlművelet a felületi szálon blokkol (11.4 lecke). Háttérbe (Ok és javítás
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:
- Felület:
txtCim,txtSzoveg,txtUt, Fájlba ment, Fájlból betölt, Adatbázisba ment, Import (tranzakció);lstJegyzetek;lstNaplo; állapotsor. - FajlSegedek:
Magyarazat(ex),OlvasBiztonsagosan,MentBiztonsagosan,MentUjraprobalva(a leckéből); a Formban nincs fájltípusú kivételkezelés. - JegyzetAdatbazis:
Letrehoz,Hozzaad(paraméteres),Mind()(olvasásSqliteDataReader-rel,usingalatt),Importal(tranzakció),AdatbazisHiba. - 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. - Ú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. - Aszinkron: minden fájl- és adatbázis-művelet
await Task.Run(...)alatt, a gombokfinally-ban visszaállnak, a kurzor is. - 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?).
- 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. - 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!)
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;
}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? Háttérszálon mentés, időtúllépéssel (Egy lehetséges megoldás
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? Kis méretű virtuális lemez (VHD) vagy kvótás mappa; vagy a hibakezelőt egységteszttel próbálod, egy hamis Egy lehetséges megoldás
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 Hiba esetén is lezárja az erőforrást (using fájl és adatbázis esetén?Megoldás megnyitása
finally): nem marad zárolt fájl vagy nyitott kapcsolat.
Közepes szint
3. Exists
Miért nem elég a Az ellenőrzés és a használat között a fájl eltűnhet vagy zárolódhat (versenyhelyzet); a File.Exists?Megoldás megnyitása
try–catch kell.
4. Paraméter
Miért paraméteres a lekérdezés? Véd az SQL-injekció ellen, és helyesen kezeli az aposztrófot és a speciális karaktereket.Megoldás megnyitása
Kihívás szint
5. Tranzakció
Mit biztosít a tranzakció? Atomitást: az összes művelet együtt érvényesül (Megoldás megnyitása
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? 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).Megoldás megnyitása
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. 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. Az ellenőrzés és a használat közötti időben megváltozó állapot miatti hiba. Átmeneti hibánál (zár, foglalt), korlátozott számban, növekvő várakozással. Ideiglenes fájlba írsz, majd cserélsz ( Visszavonja a tranzakció összes változását. SQL-injekció elleni védelem, helyes speciális karakter-kezelés. A Form ne ismerje az adatbázis-motort; egyetlen típust kezel.1. Mi a környezeti hiba?
2. Mi a TOCTOU?
3. Mikor ismételsz?
4. Hogyan mentesz biztonságosan?
File.Replace/Move).5. Mit tesz a Rollback?
6. Mi a paraméteres lekérdezés előnye?
7. Miért saját hibatípus (AdatbazisHiba)?
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
usingblokkal 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.



