Grafikus C# – Windows Forms tanfolyam › 3.2 lecke Windows Forms · 3.2 lecke · adatbázis C#-ból Az adatbázis már megvan a DB Browserben. Most a C# programmal is rá kell kapcsolódnunk. Ehhez külső csomagot telepítünk (NuGet), egy segédosztályt írunk a kapcsolat létrehozására (DB.cs), és kipróbáljuk az első lekérdezést kódból. Közben megtanuljuk a DB.cs és adatbázis-kapcsolat C#-ból
using blokkot, amely automatikusan lezárja a kapcsolatot.
A lecke végére
- NuGet csomagot telepítesz a projektbe (
System.Data.SQLite); - a
belepes.dbfájlt a program mellé másolod (Copy to Output Directory); - külön
DBosztályt írsz a kapcsolat létrehozására; - érted a
usingblokk szerepét, és használod; - az
SQLiteCommandés azExecuteScalarsegítségével lekérdezed a sorok számát; - felismered az adatbázis-kapcsolat tipikus hibáit.
Mielőtt elkezded
Ehhez a leckéhez kell az elkészült belepes.db adatbázis a 3.1 leckéből, a try–catch (2.2 lecke), és a Convert (1.3 lecke).
Az SQLite csomag telepítése (NuGet)
A C# alapból nem tud SQLite adatbázist kezelni. Ehhez egy külső csomagra van szükség. A csomagokat a NuGet csomagkezelőn keresztül telepítjük: ez egy beépített Visual Studio-szolgáltatás, amely letölti a csomagot, és hozzáadja a projekthez. A tanfolyamban a System.Data.SQLite csomagot használjuk.
- Hozz létre egy új Windows Forms projektet (0.4 lecke), például
AdatbazisProjektnéven. - A Solution Explorerben kattints jobb gombbal a projektre (nem a Solutionre), és válaszd a Manage NuGet Packages… menüpontot.
- A Browse lapon keresd meg a
System.Data.SQLitecsomagot, jelöld ki a stabil (nem előzetes, nem prerelease) változatát, majd kattints az Install gombra. - Fogadd el a licencet és a változtatások listáját (Apply, I Accept).
A csomag változata és a projekt típusa
A System.Data.SQLite több változatban létezik, és a projekt típusához kell igazodni: a .NET Framework projekthez a hagyományos csomag való, az újabb .NET projektekhez is van támogatott változat. Ha a telepítés hibát jelez, vagy a using System.Data.SQLite; sor piros marad, nézd meg a csomag leírását, hogy az adott keretrendszert támogatja-e. A tanfolyam példái a .NET Framework 4.7.2 projekten futottak. (Az újabb .NET világban gyakran használt másik csomag a Microsoft.Data.Sqlite, de a példakódok a System.Data.SQLite osztályait használják.)
Mit tegyél ide: a NuGet Package Manager ablak a System.Data.SQLite csomaggal kijelölve, a jobb oldalon az Install gombbal és a kiválasztott változattal.Javasolt fájlnév: winforms-3-2-nuget.webp · Alt szöveg: A System.Data.SQLite csomag telepítése a NuGet kezelővel
Jósolj!
A telepítés után a Solution Explorerben a projekt References (vagy Dependencies) részében mi jelenik meg? Mi változik a bin\Debug mappában a program első fordítása után?
Jóslat ellenőrzése
A References között megjelenik a System.Data.SQLite. A bin\Debug mappába a fordító átmásolja a csomag fájljait is, köztük az SQLite natív részét (SQLite.Interop.dll, általában az x86 és x64 almappákban). Ezek nélkül a program nem tudna adatbázist kezelni.
Hol van a .db fájl?
A program futáskor a bin\Debug mappából fut (az Output Directory, vagyis a kimeneti mappa), ezért az adatbázisnak is ott kell lennie. A projekt mappájából nem automatikusan másolódik át. A legegyszerűbb megoldás, ha a .db fájlt a projekthez adod, és megadod, hogy a fordító másolja:
- Másold a
belepes.dbfájlt a projekt mappájába (ahol aForm1.csvan). - A Solution Explorerben jobb gombbal a projektre → Add → Existing Item, válaszd ki a
belepes.dbfájlt. - Kattints a fájlra, és a Properties ablakban állítsd a Copy to Output Directory értékét
Copy if newer-re.
bin mappa, és a következő fordításkor újra az eredeti adatbázis másolódik át. A futás közben felvett adatok elvesznek.Mit tegyél ide: a Solution Explorer a belepes.db fájllal, mellette a Properties ablak „Copy to Output Directory: Copy if newer” beállítással.Javasolt fájlnév: winforms-3-2-copy-to-output.webp · Alt szöveg: A belepes.db fájl másolási beállítása a Properties ablakban
A DB osztály: egy helyen a kapcsolat
Az adatbázis-kapcsolat létrehozását egy külön osztályba tesszük. Ha a program több helyén is kell adatbázist elérni, nem kell mindenhol megismételni a kapcsolati szöveget, és ha változik (például más fájlnév), elég egy helyen javítani. A projekten jobb gombbal: Add → Class, a fájl neve DB.cs:
using System;
using System.Data.SQLite;
using System.IO;
using System.Windows.Forms;
internal static class DB
{
private static readonly string AdatbazisUt = Path.Combine(Application.StartupPath, "belepes.db");
public static SQLiteConnection UjKapcsolat()
{
if (!File.Exists(AdatbazisUt))
{
throw new FileNotFoundException("Nem található az adatbázis: " + AdatbazisUt);
}
return new SQLiteConnection("Data Source=" + AdatbazisUt + ";Version=3;");
}
}DB.UjKapcsolat()), mint a MessageBox.Show vagy a Clipboard esetében.bin\Debug). Így nem a futtatási környezettől függ, hol keresi a fájlt.try–catch blokkja elkapja, és üzenetet mutat.Miért ellenőrizzük a fájl létezését?
Az SQLite hajlamos hiányzó fájl esetén egy új, üres adatbázist létrehozni. Ilyenkor a program látszólag működik, de az adatbázis üres, és később a „no such table” (nincs ilyen tábla) hibát kapod. A File.Exists ellenőrzéssel ezt a hibát előre, érthetően jelezzük.
Új fogalom: a using blokk
Az adatbázis-kapcsolat egy erőforrás: használat után le kell zárni, különben a fájl zárolva maradhat, és a következő művelet hibát ad. A using blokk automatikusan gondoskodik a lezárásról, akkor is, ha közben hiba történik:
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();
// itt használhatod a kapcsolatot
} // ide érve a kapcsolat automatikusan lezárulnew SQLiteConnection(...) csak előkészíti, a megnyitás külön lépés.Close() sor kimaradna, a using viszont mindig lezár.using System.Data.SQLite; névteret ad meg. A using ( ... ) { } blokk ettől különbözik: erőforrást zár le. Ugyanaz a kulcsszó, két külön szerep.Jósolj!
Egy using blokkon belül megnyitsz egy kapcsolatot, majd a blokk közepén hiba történik (kivétel keletkezik). Lezáródik a kapcsolat?
Jóslat ellenőrzése
Igen. A using a hiba esetén is lefuttatja a lezárást, amikor a program elhagyja a blokkot. Ezért biztonságosabb, mint a kézi Close().
Az első lekérdezés kódból
Készítsünk egy kapcsolat-tesztet: egy gomb megnyitja az adatbázist, és kiírja, hány felhasználó van a belepesi_adatok táblában. A lekérdezést egy SQLiteCommand (SQL parancs) objektum hajtja végre. Tegyél az ablakra egy btnTeszt gombot és egy lblEredmeny feliratot, és a fájl elejére a using System.Data.SQLite; sort:
private void btnTeszt_Click(object sender, EventArgs e)
{
try
{
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteCommand parancs = new SQLiteCommand("SELECT COUNT(*) FROM belepesi_adatok", kapcsolat))
{
int db = Convert.ToInt32(parancs.ExecuteScalar());
lblEredmeny.Text = $"A táblában {db} felhasználó van.";
}
}
}
catch (Exception ex)
{
MessageBox.Show("Adatbázis-hiba: " + ex.Message);
}
}COUNT(*) számát. A visszatérési típusa object, ezért alakítjuk számmá a Convert.ToInt32 metódussal.Az SQLiteCommand háromféleképpen hajthatja végre az SQL-t. Később mindhármat használjuk:
| Metódus | Mire való? | Mit ad vissza? |
|---|---|---|
ExecuteScalar() | Egyetlen érték lekérdezése (például COUNT, MAX). | Az érték (object). |
ExecuteNonQuery() | Módosítás: INSERT, UPDATE, DELETE. | Az érintett sorok száma (int). |
ExecuteReader() | Több sor és oszlop lekérdezése (SELECT). | Egy olvasó objektum, amellyel sorról sorra végighaladsz. |
Jósolj!
A 3.1 leckében három felhasználót vettél fel. Mit ír ki a program a gomb megnyomása után? Mi történik, ha átnevezed a belepes.db fájlt a bin\Debug mappában?
Jóslat ellenőrzése
Kiírja: A táblában 3 felhasználó van. Ha a fájlt átnevezed, a File.Exists ellenőrzés hamis lesz, a DB osztály hibát dob, és a catch blokk üzenete jelenik meg: Adatbázis-hiba: Nem található az adatbázis: ... A program nem omlik össze.
Mit tegyél ide: két kép egymás mellett a futó programról: az egyiken a „A táblában 3 felhasználó van.” felirat, a másikon a hibaüzenet „Nem található az adatbázis” szöveggel.Javasolt fájlnév: winforms-3-2-teszt.webp · Alt szöveg: A kapcsolat teszt sikeres és hibás eredménye
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. Ismeretlen típus
// CS0246: The type or namespace name 'SQLiteConnection' could not be foundHiba és javítás
Fordítási hiba (CS0246): vagy nem telepítetted a System.Data.SQLite csomagot, vagy hiányzik a using System.Data.SQLite; sor a fájl elejéről. Ellenőrizd a NuGet telepítést, és add hozzá a névteret.
2. no such table
// SQLiteException: SQL logic error: no such table: belepesi_adatokHiba és javítás
A program olyan adatbázist nyitott meg, amelyben nincs ilyen tábla: valószínűleg egy új, üres fájlt, vagy egy másik .db fájlt a futtatási mappából. Ellenőrizd az útvonalat (Application.StartupPath), a fájl nevét, és hogy a belepes.db a bin\Debug mappába másolódott-e. A File.Exists ellenőrzés ezt előre jelzi.
3. Nem tölthető be a natív rész
// DllNotFoundException: Unable to load DLL 'SQLite.Interop.dll'Hiba és javítás
A program nem találja az SQLite natív részét (SQLite.Interop.dll), vagy az nem egyezik a program típusával (32 vagy 64 bit). Telepítsd újra a NuGet csomagot, ellenőrizd, hogy a bin\Debug mappában megvannak-e az x86 és x64 almappák, és állítsd a projekt Platform target értékét (Properties → Build) x64-re vagy x86-ra, hogy illeszkedjen a csomaghoz.
4. database is locked
// SQLiteException: database is lockedHiba és javítás
Az adatbázist egy másik program használja: legtöbbször a DB Browser, amelyben van ki nem írt változtatás (nincs Write Changes), vagy a C# programból nem zártad le a kapcsolatot. Írd ki a változtatásokat a DB Browserben (Ctrl+S), és használd mindenhol a using blokkot.
5. A kapcsolat zárva van
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
using (SQLiteCommand parancs = new SQLiteCommand("SELECT COUNT(*) FROM belepesi_adatok", kapcsolat))
{
parancs.ExecuteScalar();
}
}
// InvalidOperationException: a kapcsolat nincs megnyitvaHiba és javítás
Futás közbeni hiba (InvalidOperationException): kimaradt a kapcsolat.Open(); sor. A new SQLiteConnection csak létrehozza a kapcsolatot, a megnyitás külön lépés, amelyet a parancs végrehajtása előtt el kell végezni.
Mini projekt: kapcsolat-teszt program
Készítsd el a programot, amely biztosan eléri az adatbázist:
- Új Windows Forms projekt, NuGet:
System.Data.SQLite. - A
belepes.dba projektben,Copy if newerbeállítással. - A
DB.csosztály a leckében látott módon. - A
btnTesztgomb kiírja abelepesi_adatoktábla sorainak számát. - Egy második gomb (
btnAdatok) kiírja azadatoktábla (dolgozók) sorainak számát és a legnagyobb fizetést. - Próbáld ki hibás állapotokkal is: hiányzó
.dbfájl, átírt tábla név.
Megoldás megnyitása (próbáld ki előbb önállóan!)
private void btnAdatok_Click(object sender, EventArgs e)
{
try
{
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteCommand darab = new SQLiteCommand("SELECT COUNT(*) FROM adatok", kapcsolat))
using (SQLiteCommand maximum = new SQLiteCommand("SELECT MAX(fizetes) FROM adatok", kapcsolat))
{
int db = Convert.ToInt32(darab.ExecuteScalar());
int legnagyobb = Convert.ToInt32(maximum.ExecuteScalar());
lblEredmeny.Text = $"{db} dolgozó, a legnagyobb fizetés: {legnagyobb} Ft";
}
}
}
catch (Exception ex)
{
MessageBox.Show("Adatbázis-hiba: " + ex.Message);
}
}Mit tegyél ide: a kész kapcsolat-teszt program két gombbal és az eredményt mutató felirattal („4 dolgozó, a legnagyobb fizetés: 510000 Ft”).Javasolt fájlnév: winforms-3-2-kesz.webp · Alt szöveg: A kész kapcsolat-teszt program két gombbal
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. Az útvonal kiírása
Tegyél az ablakra egy Az út kiírása hibakereséskor sokat segít: azonnal látod, melyik fájlt nyitja meg a program.lblUt feliratot, amely megmutatja, melyik .db fájlt nyitja meg a program.Egy lehetséges megoldás
// a DB osztályban az AdatbazisUt legyen public:
public static readonly string AdatbazisUt = Path.Combine(Application.StartupPath, "belepes.db");
// a Form1_Load eseményben:
lblUt.Text = DB.AdatbazisUt;
2. Hány dolgozó él Veszprémben?
Írj egy gombot, amely az Itt az SQL szöveg fix, a program írta, nem a felhasználó. Ha a keresett város egy szövegmezőből jönne, a beírt szöveget már nem szabadna az SQL-be fűzni. Ennek a biztonságos megoldását, a paraméterezést a 3.3 lecke tanítja.adatok táblából megszámolja a Veszprém címmel rendelkező dolgozókat. Ehhez a WHERE feltételt a rögzített SQL szövegben add meg.Egy lehetséges megoldás
using (SQLiteCommand parancs = new SQLiteCommand("SELECT COUNT(*) FROM adatok WHERE cim = 'Veszprém'", kapcsolat))
{
int db = Convert.ToInt32(parancs.ExecuteScalar());
lblEredmeny.Text = $"Veszprémi dolgozók: {db}";
}
3. Hiba kipróbálása
Szándékosan hibázz: írd át az SQL szövegében a tábla nevét A belepesi_adatok-ról belepesi_adat-ra. Milyen üzenetet kapsz? Mi változik, ha kihagyod a try–catch blokkot?Egy lehetséges megoldás
catch blokk üzenete: Adatbázis-hiba: SQL logic error: no such table: belepesi_adat. Ha kihagyod a try–catch blokkot, a program a futás közben megáll, és a Visual Studio (vagy a Windows) hibaablakot mutat: a felhasználó számára ez egy összeomlás. Ezért kell a kritikus részeket hibakezeléssel védeni.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatokhoz a 3.2 lecke projektje kell: DB.cs, a belepes.db a belepesi_adatok és adatok táblával, egy lblEredmeny felirat. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Hány dolgozó van?
Egy gomb írja ki, hány sor van az Az adatok táblában.Megoldás megnyitása
ExecuteScalar a COUNT(*) eredményét adja, amelyet a Convert.ToInt32 alakít számmá.private void btnDolgozok_Click(object sender, EventArgs e)
{
try
{
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteCommand parancs = new SQLiteCommand("SELECT COUNT(*) FROM adatok", kapcsolat))
{
int db = Convert.ToInt32(parancs.ExecuteScalar());
lblEredmeny.Text = $"{db} dolgozó van az adatbázisban.";
}
}
}
catch (Exception ex)
{
MessageBox.Show("Adatbázis-hiba: " + ex.Message);
}
}
2. A using és a kézi lezárás
Írd át a fenti kapcsolatkezelést A using nélkül, try–finally szerkezettel úgy, hogy a kapcsolat hiba esetén is lezáruljon. Miért jobb mégis a using?Megoldás megnyitása
finally blokk mindig lefut, akár volt hiba, akár nem. A using ugyanezt teszi, de rövidebben, és nem lehet elfelejteni a lezárást.SQLiteConnection kapcsolat = DB.UjKapcsolat();
try
{
kapcsolat.Open();
// ... a lekérdezés ...
}
finally
{
kapcsolat.Close();
}
Közepes szint
3. Átlagfizetés
A gomb írja ki az Az adatok tábla átlagfizetését egész forintra kerekítve. Mi lesz az ExecuteScalar eredménye üres táblánál?Megoldás megnyitása
AVG tizedes számot ad, ezért a Convert.ToDouble kell. Üres táblánál az AVG értéke NULL, amelyet az ExecuteScalar DBNull.Value alakban ad vissza, és ezt nem lehet számmá alakítani. Ezért előbb ellenőrizzük.object eredmeny = parancs.ExecuteScalar();
if (eredmeny == null || eredmeny == DBNull.Value)
{
lblEredmeny.Text = "Nincs adat az átlagszámításhoz.";
}
else
{
double atlag = Convert.ToDouble(eredmeny);
lblEredmeny.Text = "Az átlagfizetés: " + Math.Round(atlag) + " Ft";
}
4. A kapcsolat állapota
Írd ki a A lblEredmeny feliratra a kapcsolat állapotát közvetlenül a megnyitás után, majd a bezárás után is. Ehhez a kapcsolatot ne using blokkban, hanem kézi Close() hívással zárd le.Megoldás megnyitása
State tulajdonság a kapcsolat állapotát adja (Open vagy Closed). A using blokk a végén magától lezárná a kapcsolatot, de akkor az állapotot már nem tudnánk megvizsgálni, ezért ennél a feladatnál a lezárást kézzel végezzük. Valódi programban a using az ajánlott megoldás.SQLiteConnection kapcsolat = DB.UjKapcsolat();
kapcsolat.Open();
string nyitva = kapcsolat.State.ToString();
kapcsolat.Close();
string zarva = kapcsolat.State.ToString();
lblEredmeny.Text = $"Megnyitás után: {nyitva}, bezárás után: {zarva}";
Kihívás szint
5. Hibák típus szerint
A kapcsolat-teszt gomb különböztesse meg a hibákat: hiányzó fájl, adatbázis-hiba (például nem létező tábla) és minden egyéb, mindegyikre külön üzenettel. A Megoldás megnyitása
catch blokkok sorrendje számít: előbb a konkrét kivételek jönnek, utoljára az általános Exception. A FileNotFoundException a System.IO névtérben van (using System.IO;), az SQLiteException a System.Data.SQLite névtérben.try
{
// ... a kapcsolat és a lekérdezés ...
}
catch (System.IO.FileNotFoundException)
{
MessageBox.Show("Az adatbázisfájl hiányzik. Ellenőrizd a Copy to Output Directory beállítást!");
}
catch (SQLiteException ex)
{
MessageBox.Show("Adatbázis-hiba (például hiányzó tábla): " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show("Váratlan hiba: " + ex.Message);
}
6. Adatbázis-információ
Egy gomb írja ki az adatbázisfájl nevét, méretét bájtban és az utolsó módosítás idejét. (Ehhez a A DB.AdatbazisUt legyen public.)Megoldás megnyitása
FileInfo osztály a fájl adatait adja: Name (név), Length (méret bájtban), LastWriteTime (utolsó módosítás). Ellenőrizd, hogy a fájl létezik-e, különben a Length hibát okozna.private void btnInfo_Click(object sender, EventArgs e)
{
System.IO.FileInfo info = new System.IO.FileInfo(DB.AdatbazisUt);
if (!info.Exists)
{
lblEredmeny.Text = "Az adatbázisfájl nem található.";
return;
}
lblEredmeny.Text = $"{info.Name}, {info.Length} bájt, módosítva: {info.LastWriteTime}";
}
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Csomagkezelő: külső csomagokat (például az SQLite támogatást) tölt le és ad a projekthez. Mert a program a Automatikusan lezárja az erőforrást (például a kapcsolatot), amikor a program elhagyja a blokkot, hiba esetén is. A Ha a lekérdezés egyetlen értéket ad vissza, például a Mert az SQLite hiányzó fájl esetén új, üres adatbázist hozhatna létre, amely később „no such table” hibát okozna. Az ellenőrzéssel a hiba előre és érthetően jelzett.1. Mire való a NuGet?
2. Miért kell a Copy to Output Directory beállítás?
bin\Debug mappából fut, és az adatbázisnak ott kell lennie. A beállítás miatt a fordító átmásolja oda.3. Mit csinál a using blokk?
4. Mi a különbség a new SQLiteConnection és az Open() között?
new csak létrehozza a kapcsolat objektumot, az Open() nyitja meg ténylegesen az adatbázist.5. Mikor használunk ExecuteScalar-t?
COUNT(*) eredményét.6. Miért ellenőrzi a DB osztály a fájl létezését?
Mit tudsz már?
- NuGet csomagot telepítesz.
- Az adatbázisfájlt a program mellé másolod.
- Egyetlen DB osztállyal hozod létre a kapcsolatot.
- A using blokkal automatikusan lezárod a kapcsolatot.
- Az ExecuteScalar segítségével egy értéket kérdezel le.
Következő lecke: paraméterezett bejelentkezés
A kapcsolat működik. A következő leckében bejelentkező ablakot készítünk az adatbázis felhasználóival.




