Grafikus C# – Windows Forms tanfolyam › 3.7 lecke

Windows Forms · 3.7 lecke · biztonság és pontosság

Paraméterezett lekérdezések és SQL-biztonság – típusok, dátumok, LIKE és IN

A paraméterek nem csak a biztonságot szolgálják: pontosabbá is teszik a programot. A 3.3 leckében megtanultad, hogy a felhasználó szövege soha nem kerülhet az SQL szövegébe. Most a gyakorlati részleteket nézzük meg: milyen típusú értéket adj át, hogyan tárolj és keress dátumot, mit tegyél a LIKE speciális karaktereivel, hogyan szűrj több városra egyszerre, és mit mutass a felhasználónak hiba esetén.

  • kb. 110 perc
  • közepes–haladó szint
  • SQL-biztonság
Diáklány C# programot készít laptopon

A lecke végére

  • érted, mit nyersz a paraméterekkel a biztonságon kívül (pontosság, aposztróf, tizedesvessző);
  • a számot előbb számmá alakítod (TryParse), és számként adod át;
  • a dátumot ISO formátumban (yyyy-MM-dd) tárolod és hasonlítod;
  • a LIKE speciális karaktereit (%, _) az ESCAPE segítségével védi;
  • változó hosszúságú IN listát építesz paraméterekkel;
  • a hibaüzenetet biztonságosan kezeled: a felhasználónak általános üzenet, a naplóba a részletek.

Mielőtt elkezded

Ehhez a leckéhez kell a 3.3 lecke (paraméterezett lekérdezés), a 3.5 lecke (LIKE, BETWEEN, IN) és az 1.3 lecke (TryParse).

3.6 INSERT, UPDATE, DELETE →

A paraméter több, mint védelem

Az összefűzött SQL nem csak biztonsági kockázat, hanem pontatlan is. Három gyakori ok:

ProblémaÖsszefűzésselParaméterrel
Aposztróf a szövegben (O'Hara)Az SQL szintaktikailag hibás lesz.Hibátlan, a szöveg csak adat.
Tizedesvessző a számban (4,5)Az SQL 4,5 két külön értéknek látja, hiba lesz.A szám típusú érték nem szövegként utazik.
Idézőjelek, speciális karakterekMeg kell védeni őket kézzel, könnyű elfelejteni.Nincs védeni való, az érték külön utazik.

Jósolj!

A txtAr mezőbe 4,5 kerül, és a program összefűzi: "... WHERE ar >= " + txtAr.Text. Mi lesz az SQL szövege, és mit csinál az adatbázis?

Jóslat ellenőrzése

Az SQL szövege ... WHERE ar >= 4,5 lesz. Az SQL-ben a vessző elválasztó jel, a tizedesjel pedig a pont, ezért az adatbázis szintaktikai hibát jelez. A paraméterezett változatnál a program előbb számmá alakítja a szöveget (double.TryParse), és a szám típusú értéket adja át, a tizedesjel kérdése eltűnik. Mellékesen: az összefűzés a felhasználó beírt szövegét is bevitte volna az SQL-be, ami a 3.3 leckében látott biztonsági kockázatot hordozza.

Típusok: számot számként, szöveget szövegként

Az AddWithValue az átadott C# érték típusából következtet az adatbázis típusára. Ha egy számot szövegként adsz át, a program működhet (az SQLite sokat megenged), de a viselkedés kiszámíthatatlanabb lesz. A jó szokás: előbb alakítsd át a bevitelt a kívánt típusra, ellenőrizd, és csak utána add át.

Form1.csC#
// ROSSZ: a szöveget közvetlenül átadjuk
parancs.Parameters.AddWithValue("@ar", txtAr.Text);

// JÓ: előbb számmá alakítjuk és ellenőrizzük
int maxAr;

if (!int.TryParse(txtAr.Text, out maxAr) || maxAr < 0)
{
Uzenet("Az ár nemnegatív egész szám legyen!", true);
return;
}

parancs.Parameters.AddWithValue("@ar", maxAr);

// még pontosabb: a típust is megadjuk
parancs.Parameters.Add("@ar", System.Data.DbType.Int32).Value = maxAr;
AddWithValue(név, érték)A kényelmes forma: a típust az érték típusából találja ki.
Parameters.Add(név, DbType).Value = értékA típust kifejezetten megadod (Int32, String, Double, DateTime). Hosszabb, de nincs kétség a típus felől.
Mit nyersz a TryParse-szal?Az érvénytelen bevitel még az adatbázis előtt kiderül, és érthető üzenetet adhatsz.
NULL átadásaHiányzó értéknél a DBNull.Value az egyértelmű választás (3.6 lecke).

Dátumok az SQLite-ban

Az SQLite-nak nincs önálló dátumtípusa: a dátumot szövegként, számként vagy időbélyegként tárolhatod. A legegyszerűbb és legbiztonságosabb megoldás a szöveges ISO formátum: yyyy-MM-dd (például 2026-06-06). Ennek két nagy előnye van: (1) a szövegesen rendezett sorrend megegyezik az időrenddel, (2) a BETWEEN pontosan működik. A magyar 2026. 06. 06. vagy a 06.06.2026 formátum szövegként rendezve rossz sorrendet ad.

Készítsünk egy kis táblát az események gyakorlásához (DB Browser, Execute SQL, majd Ctrl+S):

SQLSQLite
CREATE TABLE esemenyek (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nev TEXT NOT NULL,
datum TEXT NOT NULL, -- ISO formátum: yyyy-MM-dd
ar INTEGER NOT NULL DEFAULT 0
);

INSERT INTO esemenyek (nev, datum, ar) VALUES ('Tavaszi kirándulás', '2026-04-18', 2500);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Színházi est', '2026-05-09', 4200);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Sportnap', '2026-05-23', 0);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Bál: 100% jókedv', '2026-06-06', 3000);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Nyári tábor', '2026-07-13', 45000);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Tanévnyitó', '2026-09-01', 0);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Szülői értekezlet', '2026-09-15', 0);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Karácsonyi vásár', '2026-12-12', 1500);
INSERT INTO esemenyek (nev, datum, ar) VALUES ('Farsang_2027', '2027-02-06', 1800);

A C# programban a DateTimePicker értékét ugyanilyen szöveggé alakítjuk a paraméter átadásakor. A CultureInfo.InvariantCulture gondoskodik róla, hogy a formátum a gép nyelvi beállításától függetlenül mindig azonos legyen:

DB.cs (a fájl elején: using System.Globalization; using System.Data;)C#
private static string IsoDatum(DateTime datum)
{
return datum.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}

public static DataTable Esemenyek(string nevresz, DateTime tol, DateTime ig, int maxAr)
{
string sql = "SELECT id, nev, datum, ar FROM esemenyek "
+ "WHERE nev LIKE @minta ESCAPE '^' "
+ "AND datum BETWEEN @tol AND @ig "
+ "AND ar <= @maxar ORDER BY datum";

DataTable tabla = new DataTable();

using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();

using (SQLiteCommand parancs = new SQLiteCommand(sql, kapcsolat))
{
parancs.Parameters.AddWithValue("@minta", "%" + LikeVedelem(nevresz) + "%");
parancs.Parameters.AddWithValue("@tol", IsoDatum(tol));
parancs.Parameters.AddWithValue("@ig", IsoDatum(ig));
parancs.Parameters.AddWithValue("@maxar", maxAr);

using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(parancs))
{
adapter.Fill(tabla);
}
}
}

return tabla;
}

Jósolj!

A datum BETWEEN '2026-05-01' AND '2026-06-06' szűrés szerepel-e a június 6-i Bál és a május 23-i Sportnap? Milyen eredményt kapnál, ha a dátumokat 2026.06.06. formában tárolnád?

Jóslat ellenőrzése

Mindkettő szerepel, mert a BETWEEN a határokat is tartalmazza, és az ISO szövegek ábécé szerinti sorrendje megegyezik az időrenddel. A 2026.06.06. formátumnál a szöveges összehasonlítás karakterenként dolgozik, így a hónap és a nap sorrendje, a ponttal és a szóközökkel együtt, megbízhatatlan eredményt adhat. Mivel az SQLite szöveggé alakítva hasonlít, a dátumot mindig nagyságrendi sorrendben (év, hónap, nap) tárold.

Ha az időpont is benne van

Ha a szövegben az óra is szerepel (2026-06-06 18:30), akkor a BETWEEN '2026-06-06' AND '2026-06-06' kihagyja az aznapi eseményt, mert a 2026-06-06 18:30 hosszabb szöveg, és a végső határnál nagyobb. Ilyenkor a végdátum helyett a következő nap kezdetét használd, és < jelet: datum >= @tol AND datum < @kovetkezoNap.

A LIKE speciális karakterei és az ESCAPE

A paraméterezett LIKE védett az SQL-befecskendezés ellen, de még mindig van egy buktató: ha a felhasználó % vagy _ jelet ír a keresőmezőbe, az a mintában speciális karakter marad. A % bármit, a _ pontosan egy karaktert jelent. Egy 100% keresés így az összes eseményt visszaadná. Az ESCAPE kulcsszóval megadhatsz egy védőkaraktert: az utána álló jelet szó szerint kell érteni.

DB.csC#
// a ^ lesz a védőkarakter: a ^% jelentése szó szerint a % jel
private static string LikeVedelem(string szoveg)
{
return szoveg.Replace("^", "^^").Replace("%", "^%").Replace("_", "^_");
}

// az SQL-ben: WHERE nev LIKE @minta ESCAPE '^'
ESCAPE '^'Megmondja, hogy a ^ a védőkarakter a mintában.
Replace("^", "^^")Először magát a védőkaraktert kell megkettőzni, különben a többi cserénél keveredés lenne.
Replace("%", "^%")A % jel elé védőkarakter kerül, így szó szerinti százalékjelet jelent.
Miért nem a paraméter védi?A paraméter csak az SQL-befecskendezést akadályozza meg. A LIKE minta szabályait az adatbázis értelmezi, ezért a minta speciális karaktereit külön kell védeni.

Jósolj!

A felhasználó a _ jelet írja a keresőmezőbe, és a program a LikeVedelem nélkül, ESCAPE nélkül keres. Melyik események jönnek vissza a feladat adataiból? Mit ad a védett keresés?

Jóslat ellenőrzése

Védelem nélkül a minta %_%, vagyis „legalább egy karakter”, ezért mind a kilenc esemény megjelenik. A védett keresésnél a minta %^_%: szó szerinti alsó vonást keres, ezért csak a Farsang_2027 esemény jön vissza.

Változó hosszúságú IN lista

Ha több városra szeretnél szűrni (IN ('Győr', 'Pécs')), a lista hossza változik attól függően, hány várost jelöl a felhasználó. A teljes listát nem adhatod át egyetlen paraméterként. A megoldás: annyi paraméter, ahány elem (@v0, @v1, @v2). A paraméterek nevét a program számolja ki (sorszámokból), nem a felhasználó szövegéből, ezért az SQL szerkezete biztonságos marad:

DB.csC#
public static DataTable DolgozokVarosokban(string[] varosok)
{
DataTable tabla = new DataTable();

if (varosok.Length == 0)
{
return tabla; // az IN () üres lista hibás SQL lenne
}

// @v0, @v1, @v2 ... a program által képzett nevek
string[] nevek = new string[varosok.Length];

for (int i = 0; i < varosok.Length; i++)
{
nevek[i] = "@v" + i;
}

string sql = "SELECT id, vnev, knev, fizetes, cim FROM adatok WHERE cim IN (" + string.Join(", ", nevek) + ") ORDER BY cim, vnev";

using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();

using (SQLiteCommand parancs = new SQLiteCommand(sql, kapcsolat))
{
for (int i = 0; i < varosok.Length; i++)
{
parancs.Parameters.AddWithValue(nevek[i], varosok[i]);
}

using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(parancs))
{
adapter.Fill(tabla);
}
}
}

return tabla;
}
string[] varosokTömb (több szöveg egy változóban). A hossza a Length.
string.Join(", ", nevek)Összefűzi a tömb elemeit a megadott elválasztóval: @v0, @v1, @v2.
Mi kerül az SQL-be?Csak a @v0, @v1 alakú nevek, amelyeket egy ciklus ad. Az értékek (a városok nevei) paraméterként utaznak.
Üres listaAz IN () érvénytelen SQL, ezért előbb kilépünk, ha nincs kijelölt elem.

Biztonságos hibakezelés

Eddig a hibát az ex.Message szövegével mutattuk meg. Tanuláskor ez hasznos, mert látod, mi romlott el. Egy kész programban viszont az adatbázis hibaüzenete belső részleteket árulhat el (táblák, oszlopok és fájlok nevét, az SQL szövegét), amelyek egy támadónak vagy egy kíváncsi felhasználónak támpontot adnak. A jó gyakorlat: a felhasználó általános, érthető üzenetet kap, a részletek pedig a naplóba kerülnek, ahol a fejlesztő megnézheti.

DB.csC#
public static void Naploz(Exception ex)
{
try
{
string utvonal = System.IO.Path.Combine(Application.StartupPath, "hibanaplo.txt");
string sor = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " | " + ex.GetType().Name + " | " + ex.Message + Environment.NewLine;

System.IO.File.AppendAllText(utvonal, sor);
}
catch (Exception)
{
// a naplózás hibája ne okozzon újabb hibát
}
}

// a gombban:
catch (Exception ex)
{
DB.Naploz(ex);
MessageBox.Show("Az adatok betöltése most nem sikerült. Próbáld újra később!");
}
File.AppendAllText(út, szöveg)A szöveget egy fájl végéhez fűzi (ha nincs fájl, létrehozza).
ex.GetType().NameA kivétel típusának neve (például SQLiteException), segíti a hibakeresést.
Üres catch a naplózásbanRitka kivétel: a naplózás hibája (például nem írható mappa) nem állíthatja le a programot.
TanuláskorA tanfolyam példáiban az ex.Message látszik a MessageBox-ban, mert hibakereséshez kell. Éles programban használd ezt a naplózós változatot.

Biztonsági ellenőrzőlista az SQL-hez

(1) A felhasználó szövege csak paraméterként kerül az SQL-be. (2) Az SQL-be fűzött rész (oszlopnév, irány, paraméternevek) a program rögzített szövegeiből vagy sorszámokból áll. (3) A bevitelt a paraméter átadása előtt a megfelelő típusra alakítod és ellenőrzöd. (4) A LIKE speciális karaktereit védi az ESCAPE. (5) A hibaüzenet nem árul el belső részleteket. (6) A jelszó nem kerül naplóba vagy listába.

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 paraméter idézőjelek között

WHERE nev = '@nev'
Hiba és javítás

Az aposztrófok közé tett @nev már szó szerinti szöveg, nem paraméter: az adatbázis a @nev szöveget keresi, nem a paraméter értékét. A paraméter után nem kell idézőjel: WHERE nev = @nev. Ez az egyik leggyakoribb kezdő hiba: nincs hibaüzenet, csak üres az eredmény.

2. A LIKE minta összefűzve

string sql = "SELECT * FROM esemenyek WHERE nev LIKE '%" + txtNev.Text + "%'";
Hiba és javítás

A beírt szöveg az SQL része lett: biztonsági kockázat, és az aposztróf hibát okoz. Javítás: WHERE nev LIKE @minta, és a % jeleket a C# kód fűzi a paraméter értékéhez: "%" + LikeVedelem(nev) + "%".

3. A dátumok rossz sorrendben

Az események dátuma 06.06.2026 alakban van tárolva, és a BETWEEN furcsa eredményt ad.

Ok és javítás

A szöveges összehasonlítás a karakterek sorrendjét nézi, ezért a nap–hónap–év formátum nem időrendben rendez. Tárold a dátumot ISO formátumban (yyyy-MM-dd), és az átadásnál is ugyanezt használd az InvariantCulture megadásával.

4. A százalékjel mindent megtalál

Egy felhasználó 100% kifejezésre keres, de az összes esemény megjelenik.

Ok és javítás

A % a LIKE mintában speciális karakter. A javítás: a minta speciális karaktereit védeni kell (LikeVedelem), és az SQL-ben az ESCAPE '^' kulcsszót is meg kell adni. Csak az egyik nem elég.

5. Az IN lista üresen hiba

WHERE cim IN ()
// SQLiteException: near ")": syntax error
Hiba és javítás

Ha egyetlen város sincs kijelölve, az IN zárójele üres, és az SQL érvénytelen. A metódus elején ellenőrizd a lista hosszát: ha nulla, térj vissza üres eredménnyel, vagy jelezd a felhasználónak, hogy válasszon.

6. Szöveg a számmező helyett

parancs.Parameters.AddWithValue("@maxar", txtAr.Text);   // txtAr.Text: "2 500"
Hiba és javítás

A szóközt tartalmazó szöveg nem szám, a lekérdezés ezért nem a várt módon összehasonlít. Alakítsd előbb számmá (int.TryParse), jelezz hibás bevitelnél, és számként add át. Ügyelj arra is, hogy a TryParse a gép nyelvi beállítása szerint értelmez (például az ezres tagoló szóközt vagy a tizedesvesszőt).

Mini projekt: eseménykereső

Készíts keresőt az esemenyek táblához:

  1. Az ablakon: txtNev (névrészlet), dtpTol és dtpIg (dátumsáv), nudMaxAr (legnagyobb ár, alapérték 100000), egy dgvEsemenyek táblázat és egy lblDarab felirat.
  2. A DB.Esemenyek metódus a leckében látott módon: LIKE ... ESCAPE, BETWEEN ISO dátumokkal, az ár szűrője.
  3. Ha a kezdő dátum a vég utáni, a program jelezzen, és ne kérdezzen.
  4. Minden szűrő változása frissítse a listát (a betoltve védelemmel, mint a 3.5 leckében).
  5. Teszteld: 100%, _, ékezetes betűk, egy napos sáv, nulla találat.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A DB.Esemenyek a leckében szerepel. Az ablak kódja a 3.5 lecke keresőjét követi:

Form1.csC#
private bool betoltve = false;

private void Form1_Load(object sender, EventArgs e)
{
dtpTol.Value = new DateTime(2026, 1, 1);
dtpIg.Value = new DateTime(2027, 12, 31);
nudMaxAr.Value = 100000;

betoltve = true;
Frissit();
}

private void Frissit()
{
if (!betoltve)
{
return;
}

if (dtpTol.Value.Date > dtpIg.Value.Date)
{
lblDarab.Text = "A kezdő dátum nem lehet a vég után!";
return;
}

try
{
DataTable tabla = DB.Esemenyek(txtNev.Text.Trim(), dtpTol.Value, dtpIg.Value, (int)nudMaxAr.Value);

dgvEsemenyek.DataSource = tabla;
lblDarab.Text = tabla.Rows.Count == 0 ? "Nincs találat." : tabla.Rows.Count + " találat";
}
catch (Exception ex)
{
DB.Naploz(ex);
MessageBox.Show("Az adatok betöltése most nem sikerült.");
}
}

private void Szuro_Valtozott(object sender, EventArgs e)
{
Frissit();
}
📷 Képernyőkép helye

Mit tegyél ide: a kész eseménykereső egy 100% kereséssel, amelyben csak a „Bál: 100% jókedv” esemény látszik.Javasolt fájlnév: winforms-3-7-kesz.webp · Alt szöveg: Az eseménykereső a százalékjelre védetten keres

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. Több város egyszerre

Egy clbVarosok CheckedListBox mutassa a városokat (SELECT DISTINCT cim). A btnSzur gomb csak a bejelölt városok dolgozóit mutassa a táblázatban. Ha nincs bejelölt város, szóljon.

Egy lehetséges megoldás

A bejelölt elemeket (CheckedItems) egy tömbbe gyűjtjük, amelyet a DB.DolgozokVarosokban metódus kap meg (lásd az 5. szakaszt).

Form1.csC#
private void btnSzur_Click(object sender, EventArgs e)
{
if (clbVarosok.CheckedItems.Count == 0)
{
lblDarab.Text = "Jelölj be legalább egy várost!";
return;
}

string[] valasztott = new string[clbVarosok.CheckedItems.Count];

for (int i = 0; i < valasztott.Length; i++)
{
valasztott[i] = clbVarosok.CheckedItems[i].ToString();
}

dgvDolgozok.DataSource = DB.DolgozokVarosokban(valasztott);
}

2. Események betöltése szövegből

Egy többsoros txtLista mezőbe soronként így gépelnek eseményeket: Szalagavató;3500;2026-11-20. A btnBetolt gomb vegye fel őket az adatbázisba. A hibás sorokat hagyja ki, és a végén írja ki, hány sor került be és hány hibás volt.

Egy lehetséges megoldás

Egyetlen parancsobjektumot használunk, és ciklusonként csak a paraméterek értékét cseréljük. Minden sort ellenőrzünk (három rész, az ár szám, a dátum érvényes), és a dátumot ISO formátumra alakítjuk. Sok sornál (több száz) egy tranzakcióban érdemes futtatni, ezt a 3.10 leckében tanuljuk.

Form1.csC#
private void btnBetolt_Click(object sender, EventArgs e)
{
int jo = 0;
int hibas = 0;

using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();

using (SQLiteCommand parancs = new SQLiteCommand("INSERT INTO esemenyek (nev, datum, ar) VALUES (@nev, @datum, @ar)", kapcsolat))
{
parancs.Parameters.Add("@nev", System.Data.DbType.String);
parancs.Parameters.Add("@datum", System.Data.DbType.String);
parancs.Parameters.Add("@ar", System.Data.DbType.Int32);

foreach (string sor in txtLista.Lines)
{
if (sor.Trim().Length == 0)
{
continue;
}

string[] reszek = sor.Split(';');
int ar;
DateTime datum;

if (reszek.Length != 3 || reszek[0].Trim().Length == 0 || !int.TryParse(reszek[1], out ar) || !DateTime.TryParse(reszek[2], out datum))
{
hibas = hibas + 1;
continue;
}

parancs.Parameters["@nev"].Value = reszek[0].Trim();
parancs.Parameters["@datum"].Value = datum.ToString("yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
parancs.Parameters["@ar"].Value = ar;
parancs.ExecuteNonQuery();
jo = jo + 1;
}
}
}

lblEredmeny.Text = $"{jo} sor bekerült, {hibas} hibás volt.";
}

3. Általános hibaüzenet és napló

Írd át a dolgozói kereső (3.5) hibakezelését: a felhasználó általános üzenetet kapjon, a részletek pedig kerüljenek a hibanaplo.txt fájlba. Hogyan ellenőrzöd, hogy működik?

Egy lehetséges megoldás

Hibát például úgy idézhetsz elő, hogy ideiglenesen elírod a tábla nevét az SQL-ben. A program mutasson általános üzenetet, a hibanaplo.txt (a bin mappában) pedig tartalmazza a részleteket. A teszt végén javítsd vissza a nevet.

Form1.csC#
catch (Exception ex)
{
DB.Naploz(ex);
MessageBox.Show("A lista betöltése most nem sikerült.");
}

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. Az eseménykereső projektje és az esemenyek tábla kell hozzá. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Melyik biztonságos?

Döntsd el, hogy melyik kódrészlet biztonságos, és melyik nem:
(a) "... WHERE nev = @nev" és AddWithValue("@nev", txtNev.Text)
(b) "... WHERE nev = '" + txtNev.Text + "'"
(c) "... ORDER BY " + sorrend, ahol a sorrend egy if–else láncból kapja a szöveget
(d) "... ORDER BY " + cmbOszlop.Text

Megoldás megnyitása

(a) biztonságos: paraméterezett érték. (b) nem biztonságos: a felhasználó szövege az SQL része. (c) biztonságos: a program rögzített szövegeiből áll. (d) nem biztonságos: a legördülő lista szövege (a Text) módosítható, ha szerkeszthető a lista, és más helyről is jöhet. Rögzített lista esetén is jobb a sorszámra (SelectedIndex) építeni, és a szövegeket a kódban tartani.

2. Javítsd a lekérdezést

Javítsd ki: SELECT * FROM esemenyek WHERE nev LIKE '%@minta%'.

Megoldás megnyitása

A paraméter az aposztrófok között szó szerinti szöveg lett, ráadásul a % jelek is az SQL-ben vannak. A helyes forma: WHERE nev LIKE @minta, és a C# kód állítja össze a mintát: "%" + szoveg + "%".

C#C#
string sql = "SELECT * FROM esemenyek WHERE nev LIKE @minta";
parancs.Parameters.AddWithValue("@minta", "%" + txtNev.Text + "%");

Közepes szint

3. Ár ellenőrzése átadás előtt

A txtMaxAr mezőbe írt szöveget alakítsd számmá, ellenőrizd (nemnegatív egész), és csak utána add át a lekérdezésnek. Hibás bevitelnél az ErrorProvider jelezzen.

Megoldás megnyitása

A TryParse sikerét és az érték tartományát is vizsgáljuk. Hibánál a hibajel a mező mellett jelenik meg (1.11 lecke), és a lekérdezés nem fut le.

Form1.csC#
int maxAr;

if (!int.TryParse(txtMaxAr.Text, out maxAr) || maxAr < 0)
{
epHiba.SetError(txtMaxAr, "Nemnegatív egész számot adj meg!");
return;
}

epHiba.SetError(txtMaxAr, "");
dgvEsemenyek.DataSource = DB.Esemenyek(txtNev.Text.Trim(), dtpTol.Value, dtpIg.Value, maxAr);

4. Időponttal tárolt dátum

A tábla dátuma mostantól időt is tartalmaz (2026-06-06 18:30). Írd át a szűrést úgy, hogy a kiválasztott záró nap eseményei is megjelenjenek.

Megoldás megnyitása

A záró határt a következő nap kezdetére állítjuk, és < jellel hasonlítunk. A DateTime.AddDays(1) a következő napot adja.

DB.csC#
string sql = "SELECT id, nev, datum, ar FROM esemenyek WHERE datum >= @tol AND datum < @kovetkezo ORDER BY datum";

parancs.Parameters.AddWithValue("@tol", IsoDatum(tol));
parancs.Parameters.AddWithValue("@kovetkezo", IsoDatum(ig.AddDays(1)));

Kihívás szint

5. Pontos szó szerinti keresés

Hogyan működne az alsó vonásra (_) és a százalékjelre (%) való keresés a LikeVedelem nélkül, és mi a javítás? Írj tesztszöveget is.

Megoldás megnyitása

Védelem nélkül mindkét jel helyettesítő karakter marad: a _ egy tetszőleges karaktert, a % bármennyi karaktert jelent, ezért a keresés túl sok találatot ad. A javítás a LikeVedelem metódus és az ESCAPE '^' együtt. Teszteld: 100% → egy találat; _ → egy találat (Farsang_2027); ^ → nulla találat (és nem hiba); a% → csak a szó szerint a% tartalmazó nevek (nulla).

6. Kódátnézés

Keress legalább öt hibát (biztonsági vagy pontossági) az alábbi kódban, és javítsd őket:

string sql = "SELECT * FROM esemenyek WHERE nev LIKE '%" + txtNev.Text + "%' AND datum >= '"
+ dtpTol.Text + "' AND ar <= " + txtAr.Text + " ORDER BY " + cmbRend.Text;

try
{
// ... végrehajtás ...
}
catch (Exception ex)
{
MessageBox.Show(sql + Environment.NewLine + ex.Message);
}
Megoldás megnyitása

(1) A nev beírt szövege összefűzve: SQL-befecskendezés, paraméter kell. (2) A LIKE speciális karaktereit nem védi az ESCAPE. (3) A dtpTol.Text a gép nyelvi formátumában (nem ISO) kerül be, ezért a dátumok összehasonlítása rossz. (4) Az txtAr.Text szám helyett szövegként van összefűzve: nincs TryParse, és ez is befecskendezési felület. (5) Az ORDER BY a cmbRend.Text szövegét fűzi: rögzített szövegekből kell választani a SelectedIndex alapján. (6) A hibaüzenet kiírja az SQL szövegét és a belső hibát a felhasználónak: általános üzenet és napló kell.

Gyors önellenőrzés

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

1. Miért pontosabb a paraméter az összefűzésnél?

Mert az érték külön utazik, ezért az aposztróf, a tizedesvessző és a speciális karakterek nem borítják fel az SQL szerkezetét.

2. Miért kell a bevitelt előbb számmá alakítani?

Hogy a hibás bevitel az adatbázis előtt kiderüljön, és a paraméter szám típusú legyen, ne szöveg.

3. Hogyan tárold a dátumot az SQLite-ban?

ISO szöveges formátumban (yyyy-MM-dd), mert így a szöveges sorrend megegyezik az időrenddel.

4. Mit védi az ESCAPE?

A LIKE minta speciális karaktereit (%, _): a védőkarakter után álló jel szó szerint értendő.

5. Hogyan szűrsz változó hosszúságú listára?

Annyi paramétert használsz, ahány elem van (@v0, @v1, ...). A paraméterek neveit a program képzi sorszámokból.

6. Mit kapjon a felhasználó hiba esetén?

Általános, érthető üzenetet. A részletek a naplóba kerülnek.

7. Mi a baj a WHERE nev = '@nev' alakkal?

Az aposztrófok között a @nev szó szerinti szöveg, nem paraméter.

Mit tudsz már?

  • A bevitelt a paraméter előtt típusra alakítod és ellenőrzöd.
  • A dátumot ISO formátumban tárolod és szűröd.
  • A LIKE speciális karaktereit az ESCAPE védi.
  • Változó hosszúságú IN listát építesz biztonságosan.
  • A hibákat általános üzenettel és naplóval kezeled.

Következő lecke: DataAdapter, DataTable és DataSource

A lekérdezéseket már biztonságosan írod. A következő leckében a letöltött adatokkal dolgozunk a program oldalán: szűrés, rendezés és keresés a DataTable-ben.