Grafikus C# – Windows Forms tanfolyam › 14.4 lecke

Windows Forms · 14.4 lecke · build

NuGet-függőségek

Nem kell mindent magadnak megírnod: a CSV-olvasót, a naplózót, az adatbázis-meghajtót mások már megírták és kipróbálták. Ezeket NuGet-csomagként építheted be a programodba, néhány kattintással. De minden külső könyvtár egyben függőség is: a program csak akkor fut, ha az a könyvtár ott van a kimeneti mappában, a verziója megfelelő, a licence engedi a használatot, és biztonsági hibája nincs. Ebben a leckében megtanulod a csomagok hozzáadását és kezelését (PackageReference), a verziók és a tranzitív függőségek működését, azt, hogy a csomagok fájljai hogyan kerülnek a kimenetbe (a natív DLL-ekkel együtt), hogyan választasz megbízható csomagot, és hogyan frissítesz biztonságosan.

  • kb. 90 perc
  • közepes szint
  • NuGet
Anime stílusú fiú és lány egy C# programozási feladatot beszél meg

A lecke végére

  • csomagot adsz a projekthez (Visual Studio és dotnet add package), és érted a PackageReference-t;
  • ismered a verziószámozást (major.minor.patch) és a tranzitív függőséget;
  • tudod, hogyan kerülnek a csomag DLL-jei és natív fájljai a kimenetbe;
  • a csomagot több szempont szerint választod (karbantartás, licenc, biztonság, méret);
  • listázod és frissíted a csomagokat (dotnet list package --outdated);
  • kezeled a tipikus NuGet-hibákat (restore, verzióütközés, natív könyvtár).

Mielőtt elkezded

Ehhez a leckéhez a 14.2–14.3 lecke szükséges. A 13.6 lecke SQLite-példája már használt NuGet-csomagot (Microsoft.Data.Sqlite).

14.3 Futtatókörnyezet →

Csomag hozzáadása és a PackageReference

Két mód ugyanarraparancsok
Visual Studio:  Jobb klikk a projekten → Manage NuGet Packages... → Browse → keresés → Install

Parancssor: dotnet add package Microsoft.Data.Sqlite
dotnet add package CsvHelper --version 33.0.1
dotnet remove package CsvHelper
dotnet restore (a csomagok letöltése a .csproj alapján)
dotnet list package (a használt csomagok és verziók)
dotnet list package --outdated (az elavultak)
dotnet list package --vulnerable (ismert biztonsági hibával)
dotnet list package --include-transitive (a közvetett függőségekkel együtt)
Konyvtar.App.csprojXML
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0" />
<PackageReference Include="CsvHelper" Version="33.0.1" />
</ItemGroup>
PackageReferenceA projektfájl csak a csomag nevét és verzióját tárolja (nem a DLL-t). A dotnet restore (vagy a Visual Studio fordításkor) letölti a csomagot a gép közös gyorsítótárába (%UserProfile%\.nuget\packages), és onnan kerül a kimenetbe. A csomag fájljait nem tárolod a projektben és a verziókezelésben.
VerziószámozásA csomagok általában a SemVer (major.minor.patch) szabályt követik: a patch (hibajavítás) és a minor (új, kompatibilis funkció) biztonságos; a major (7.0 → 8.0) kompatibilitást törhet: a frissítés után ellenőrizni kell a kódot.
Tranzitív függőségA csomagnak is lehetnek saját függőségei: ezek automatikusan is a projektedbe kerülnek (--include-transitive). Így egy csomag hozzáadása több DLL-t is jelenthet.
Biztonságos forrásA csomagok alapértelmezett forrása a nuget.org. Cégek saját forrást is használnak. Mindig tudd, honnan töltesz le csomagot: a hasonló nevű, rosszindulatú csomagok (typosquatting) valódi veszélyt jelentenek.

Jósolj!

Egy barátod megkapja a projekted mappáját (a bin és obj nélkül), megnyitja, és fordítja. A CsvHelper DLL nincs a mappában. Lefordul-e?

Jóslat ellenőrzése

Igen (ha van internet): a Visual Studio vagy a dotnet restore a .csproj PackageReference sora alapján letölti a csomagot (package restore). Offline gépen a fordítás NU1101/NU1301 hibával áll meg, kivéve, ha a csomag a gép gyorsítótárában már megvan.

Mi kerül a kimenetbe?

bin\Release\net8.0-windows\ (részlet)mappák
Konyvtar.App.exe, Konyvtar.App.dll, .runtimeconfig.json, .deps.json   ← a program
CsvHelper.dll ← NuGet-csomag (kezelt kód)
Microsoft.Data.Sqlite.dll ← NuGet-csomag
SQLitePCLRaw.core.dll, SQLitePCLRaw.provider... ← tranzitív függőségek
runtimes\
win-x64\native\e_sqlite3.dll ← NATÍV könyvtár (nem .NET kód!), architektúránként
win-x86\native\e_sqlite3.dll
linux-x64\native\libe_sqlite3.so ← más platformokhoz (Windowson nem kell)
Kezelt és natív kódA legtöbb csomag kezelt .NET DLL. Egyes csomagok (adatbázis-meghajtók, képfeldolgozók) natív (C/C++) könyvtárat is hoznak a runtimes\<platform>\native mappában. A natív DLL-nek a program platformjához kell illeszkednie (x64/x86): hibás platformnál DllNotFoundException/BadImageFormatException.
Mind kell az átadáshozA csomagok DLL-jei és a runtimes mappa a program része: az átadandó mappából nem hiányozhat (14.2 lecke). A Publish (14.5 lecke) ezt a megfelelő, egy platformra szűrt tartalommal gyűjti össze.
A .deps.json szerepeA .deps.json felsorolja a csomagokat és a natív fájlokat: a futtatókörnyezet ebből tudja, mit hol keressen. Ne töröld, és ne szerkeszd kézzel.
MéretMinden csomag növeli a kimenet méretét és a program indítási idejét. Egy kis segédfunkcióért ne húzz be egy nagy csomagot (például egy sor szövegművelethez egy teljes „segédkönyvtárat”).

Jósolj!

A programodat 64 bites gépen fejlesztetted, és az átadott mappában csak a win-x86\native\e_sqlite3.dll van (a win-x64 mappát kihagyták). Mi történik az SQLite használatakor 64 bites gépen?

Jóslat ellenőrzése

A program a 64 bites folyamatban a 64 bites natív könyvtárat keresi, nem találja: DllNotFoundException (vagy Unable to load DLL 'e_sqlite3') hibával elszáll az első adatbázis-használatkor. Ezért kell a teljes runtimes mappa (vagy a Publish).

Hogyan válassz csomagot?

SzempontMit nézz?
Szükséges-e?Nincs-e a keretrendszerben már (System.Text.Json, System.IO, HttpClient)? Egy kis feladathoz írj saját kódot.
KarbantartásMikor volt az utolsó kiadás? Aktív-e a fejlesztő? Hány megoldatlan hiba van?
Népszerűség és hírnévLetöltések száma, ismert szervezet (Microsoft, .NET Foundation), dokumentáció.
LicencMIT, Apache 2.0, BSD: általában szabadon használható. GPL: másolt vagy terjesztett programra kötelezettség. Kereskedelmi/saját licenc: ellenőrizd a feltételeket. A licenc a NuGet-oldalon és a csomagban van.
BiztonságIsmert sebezhetőség (dotnet list package --vulnerable, a NuGet-oldal figyelmeztetése).
FüggőségekHány további csomagot hoz? Natív kódot tartalmaz-e (platformfüggés)?
KompatibilitásTámogatja-e a cél-keretrendszeredet (net8.0, netstandard2.0)? Az NU1701 figyelmeztetés régebbi, nem teljesen kompatibilis csomagra utal.
A licenc kötelezettségHa a programodat másnak adod át, a használt csomagok licenc-feltételeit be kell tartanod: általában a szerzői jogi nyilatkozat és a licenc szövegének mellékelése (harmadik fél licencei fájl). A „szabad” nem azt jelenti, hogy nincs feltétel.
Biztonság: a láncolatA csomag a programod része: egy sebezhető vagy rosszindulatú csomag a te programodat teszi sebezhetővé. Ezért: csak ismert forrásból, pontos névvel, és rendszeres ellenőrzéssel (--vulnerable).

Jósolj!

Egy csomag utolsó kiadása 6 éve volt, 40 megoldatlan hibabejelentéssel, és egy ismert biztonsági figyelmeztetéssel. Mégis ez tudja pontosan azt, amire szükséged van. Mit teszel?

Jóslat ellenőrzése

Keress alternatívát (karbantartott csomag vagy keretrendszeri megoldás), vagy írd meg a szükséges részt magad. Egy elhagyott, sebezhető csomag hosszú távon kockázat: a hibákat senki sem javítja, és minden felhasználód viseli a következményt.

Frissítés és verziókezelés

Biztonságos frissítés lépései

  1. Listázd: dotnet list package --outdated és --vulnerable.
  2. Olvasd el a kiadási jegyzetet (release notes, breaking changes), főleg major váltásnál.
  3. Frissíts egyet egyszerre (dotnet add package X --version Y).
  4. Fordítsd le, nézd a figyelmeztetéseket (Error List).
  5. Futtasd a teszteket (13.8 lecke): regressziós készlet.
  6. Naplózd a változást (melyik csomag, melyik verzióról melyikre, miért).

Szabályok

  • A biztonsági frissítés (sebezhetőség) sürgős: ne halogasd.
  • Kiadás előtt ne frissíts mindent: új hibát hozhat. Előbb a teszteléssel ellenőrizd.
  • Rögzített verzió: a Version="33.0.1" pontos; a lebegő verzió (33.*) kiszámíthatatlan.
  • Vedd ki a nem használt csomagot (dotnet remove package).
  • Licencjegyzék: tartsd naprakészen, mely csomag mely licenccel van a programban.
  • Reprodukálható build: a packages.lock.json (RestorePackagesWithLockFile) rögzíti a pontos verziókat, a tranzitívakat is.
VerzióütközésHa két csomag ugyanannak a harmadiknak más verzióját kéri, a NuGet a magasabbat választja; a lefelé irányuló eltérés NU1605 hiba (a downgrade). Megoldás: a közös csomag verzióját közvetlenül add meg a projektben a megfelelő szinten.
Offline fordításHa nincs internet, a fordítás csak akkor megy, ha a csomagok a gyorsítótárban vannak. Fontos build-gépen érdemes helyi csomagforrást (offline feed) tartani.

Jósolj!

A CsvHelper verzióját 33.0.1-ről 34.0.0-ra emeled (major váltás), és a fordítás hibátlan. Elég-e ennyi ellenőrzés?

Jóslat ellenőrzése

Nem: a fordítás csak a nyelvi kompatibilitást ellenőrzi. A major verzió viselkedése is változhat (alapértelmezett beállítások, kerekítés, elválasztó), ezért a programot futtatni és tesztelni kell (13.8 lecke), és el kell olvasni a változásjegyzéket.

📷 Képernyőkép helye

Mit tegyél ide: a NuGet csomagkezelő ablak (telepített csomagok, frissítések) és a dotnet list package kimenete.Javasolt fájlnév: winforms-14-4-nuget.webp · Alt szöveg: A NuGet csomagkezelő

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. NU1101: nem található a csomag

A fordítás a csomag letöltésénél áll meg.

Ok és javítás

Elírt csomagnév, nincs internet, vagy a csomagforrás nem elérhető. Ellenőrizd a nevet és a forrást (Tools → NuGet Package Manager → Package Sources).

2. NU1605: csomag-visszalépés

A hiba szerint egy csomagot alacsonyabb verzióra „frissítene”.

Ok és javítás

Verzióütközés: add meg a közvetlenül használt csomagot a magasabb verzióval a .csproj-ban.

3. Fordul, de futáskor FileNotFoundException

A másik gépen a csomag DLL-je hiányzik.

Ok és javítás

Az átadott mappából kimaradt egy DLL (14.2 lecke). Adj át teljes kimenetet vagy Publish csomagot (14.5 lecke).

4. DllNotFoundException az adatbázisnál

A program SQLite-hívásnál elszáll a másik gépen.

Ok és javítás

A natív könyvtár (runtimes\win-x64\native) hiányzik, vagy a platform nem egyezik. Ellenőrizd a runtimes mappát és a platformcélt (14.1 lecke).

5. NU1903: ismert sebezhetőség

A fordítás figyelmeztetést ad egy csomagra.

Ok és javítás

A csomag (vagy egy tranzitív függősége) sebezhető. Frissítsd a javított verzióra (--include-transitive segít megtalálni), majd teszteld.

6. A frissítés után másképp működik

A csomag frissítése után a CSV kimenet formátuma megváltozott.

Ok és javítás

A major váltás viselkedést változtatott. Olvasd el a változásjegyzéket, állítsd be a megfelelő konfigurációt (például elválasztó), vagy rögzítsd a régi verziót, amíg a kódot nem igazítod. A regressziós teszt (13.8 lecke) ezt kapta volna el.

Mini projekt: függőség-audit

Készíts egy kis programot két NuGet-csomaggal, majd végezz függőség-auditot: mit hoz a két csomag, mi kerül a kimenetbe, milyen licencek és biztonsági állapot tartozik hozzájuk:

  1. Projekt: Windows Forms (Audit); btnExport (Lista exportja CSV-be), btnDb (Mentés SQLite-ba), lstBetoltott (betöltött assembly-k), lblUzenet.
  2. Csomagok: add hozzá a CsvHelper és a Microsoft.Data.Sqlite csomagot (Visual Studio vagy dotnet add package); jegyezd fel a telepített verziókat.
  3. Kód: másold be a lenti kódot (CSV-export és SQLite-mentés, a betöltött assembly-k listázása).
  4. Parancsok: futtasd a dotnet list package, dotnet list package --include-transitive, --outdated és --vulnerable parancsot; írd le a kimenetet.
  5. Kimenet: fordítsd Release módban, és jegyezd fel a bin\Release mappa DLL-jeit és a runtimes mappa tartalmát; melyik a kezelt, melyik a natív?
  6. Tiszta restore: töröld a bin és obj mappát, majd futtasd a dotnet restore és dotnet build -c Release parancsot; a csomagok újra letöltődnek-e?
  7. Licenc-tábla: minden közvetlen csomagnál (a NuGet-oldalról): név, verzió, licenc, utolsó kiadás, függőségek száma, natív?, szükséges-e valóban?.
  8. Frissítés próba: frissíts egy csomagot egy újabb patch vagy minor verzióra, fordíts, és futtasd a funkciót (CSV, SQLite); jegyezd fel az eredményt, és állítsd vissza, ha hibás.
  9. Alternatíva: írd le, hogyan oldanád meg a CSV-exportot csomag nélkül (egyszerű StringBuilder), és mikor érné meg inkább a csomag.
Megoldás megnyitása (próbáld ki előbb önállóan!)
Form1.cs (using CsvHelper; using Microsoft.Data.Sqlite; using System.Globalization; using System.IO; using System.Linq;)C#
public class Termek { public int Id { get; set; } public string Nev { get; set; } public decimal Ar { get; set; } }

private readonly List<Termek> lista = new List<Termek>
{
new Termek { Id = 1, Nev = "Alma", Ar = 120.5m },
new Termek { Id = 2, Nev = "Körte, érett", Ar = 200m } // a vessző a CsvHelper idézi: nem rontja el a CSV-t
};

private void Form1_Load(object sender, EventArgs e)
{
foreach (var a in AppDomain.CurrentDomain.GetAssemblies().OrderBy(x => x.GetName().Name))
{
lstBetoltott.Items.Add(a.GetName().Name + " " + a.GetName().Version);
}
}

private void btnExport_Click(object sender, EventArgs e)
{
string ut = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "termekek.csv");

try
{
using (StreamWriter w = new StreamWriter(ut, false, new System.Text.UTF8Encoding(true)))
using (CsvWriter csv = new CsvWriter(w, CultureInfo.InvariantCulture))
{
csv.WriteRecords(lista);
}

lblUzenet.Text = "CSV mentve: " + ut;
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { lblUzenet.Text = "A mentés nem sikerült: " + ex.Message; }
}

private void btnDb_Click(object sender, EventArgs e)
{
string db = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Audit", "termekek.db");

try
{
Directory.CreateDirectory(Path.GetDirectoryName(db));

using (SqliteConnection kap = new SqliteConnection("Data Source=" + db))
{
kap.Open();

using (SqliteCommand c = kap.CreateCommand())
{
c.CommandText = "CREATE TABLE IF NOT EXISTS termek (id INTEGER PRIMARY KEY, nev TEXT, ar REAL)";
c.ExecuteNonQuery();
}

foreach (Termek t in lista)
{
using (SqliteCommand c = kap.CreateCommand())
{
c.CommandText = "INSERT OR REPLACE INTO termek (id, nev, ar) VALUES ($i, $n, $a)";
c.Parameters.AddWithValue("$i", t.Id);
c.Parameters.AddWithValue("$n", t.Nev);
c.Parameters.AddWithValue("$a", (double)t.Ar);
c.ExecuteNonQuery();
}
}
}

lblUzenet.Text = "Adatbázis mentve: " + db;
}
catch (SqliteException ex) { lblUzenet.Text = "Adatbázis-hiba: " + ex.Message; }
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { lblUzenet.Text = "Fájlhiba: " + ex.Message; }
}

Várható megfigyelések: a CsvHelper.dll és a Microsoft.Data.Sqlite.dll mellett a tranzitív SQLitePCLRaw.* DLL-ek és a runtimes\win-*\native\e_sqlite3.dll kerül a kimenetbe. A csomagok licencei és verziói a NuGet-oldalon olvashatók (a leckében szándékosan nincsenek rögzített számok, mert a csomagok változnak); a saját táblázatodat a valós, aktuális adatokkal töltsd ki. Az alternatíva: kis, egyszerű CSV-nél a StringBuilder és a mezők kézi idézése elég, de a vesszős és idézőjeles szöveg kezelése hibalehetőség; a csomag megéri.

📷 Képernyőkép helye

Mit tegyél ide: a függőség-audit táblázata és a dotnet list package --include-transitive kimenete.Javasolt fájlnév: winforms-14-4-kesz.webp · Alt szöveg: A függőség-audit eredménye

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. Harmadik fél licencei

Készíts THIRD-PARTY-NOTICES.txt fájlt a programod csomagjaihoz.

Egy lehetséges megoldás

Minden csomagnál: név, verzió, szerző, licenc neve, a licenc teljes szövege (a NuGet-oldalon vagy a csomagban). A fájl a program mellé kerül (a kimenetbe másolva, 14.2 lecke), és a Névjegy ablakból elérhető (14.8 lecke).

2. Lock fájl

Kapcsold be a packages.lock.json használatát, és figyeld meg, mi változik.

Egy lehetséges megoldás

A .csproj-ban <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>; a restore létrehozza a fájlt (minden csomag pontos verziójával, a tranzitívakkal együtt). A fájl a forráskódba kerül; dotnet restore --locked-mode hibát jelez, ha a verziók eltérnek.

3. Elavult csomagok

Futtasd a dotnet list package --outdated parancsot egy régi projekten, és döntsd el: mit frissítesz azonnal, mit később?

Egy lehetséges megoldás

Sürgős: a sebezhetőségi figyelmeztetéssel jelölt csomagok. Később, teszteléssel: a major frissítések (breaking change). Rutinszerűen: patch és minor, regressziós teszt után.

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. Mi a NuGet?

Fogalmazd meg egy mondatban.

Megoldás megnyitása

A .NET csomagkezelője: kész könyvtárakat adhatsz a projekthez verzióval együtt.

2. Hol a csomag?

Mit tárol a .csproj?

Megoldás megnyitása

A csomag nevét és verzióját (PackageReference), nem a DLL-t; a restore tölti le.

Közepes szint

3. Tranzitív

Mi a tranzitív függőség?

Megoldás megnyitása

A csomag saját függősége, amely automatikusan a projektedbe kerül.

4. SemVer

Melyik verzióváltás lehet törő: 1.2.3 → 1.2.4, 1.2.3 → 1.3.0, vagy 1.2.3 → 2.0.0?

Megoldás megnyitása

A major váltás (2.0.0): kompatibilitást törhet; a patch és a minor általában biztonságos.

Kihívás szint

5. Natív könyvtár

Miért okozhat gondot a natív könyvtár?

Megoldás megnyitása

Platformfüggő (x64/x86): ha nem illeszkedik, DllNotFoundException vagy BadImageFormatException keletkezik.

6. Mikor ne csomag?

Mikor jobb saját kód?

Megoldás megnyitása

Ha a feladat kicsi, és a keretrendszerben megvan; mert minden csomag méretet, biztonsági és karbantartási terhet jelent.

Gyors önellenőrzés

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

1. Mi a PackageReference?

A projektfájl sora, amely a csomag nevét és verzióját rögzíti.

2. Mit tesz a dotnet restore?

Letölti a .csproj szerinti csomagokat.

3. Mi a typosquatting?

Hasonló nevű, rosszindulatú csomag: ezért pontos névvel és ismert forrásból tölts.

4. Mit mutat a --vulnerable?

Az ismert biztonsági hibával jelölt csomagokat.

5. Mi a runtimes mappa?

A platformspecifikus (natív) könyvtárak helye a kimenetben.

6. Mire kell figyelni licencnél?

Hogy a feltételek (szerzői jogi nyilatkozat, GPL kötelezettség) betarthatók-e a programod terjesztésekor.

7. Hogyan frissítesz biztonságosan?

Egyet egyszerre, a változásjegyzék elolvasásával, fordítással és regressziós teszttel.

Mit tudsz már?

  • Csomagot adsz a projekthez, és érted a PackageReference, a verziók és a tranzitív függőségek működését.
  • Tudod, mi kerül a kimenetbe (kezelt és natív fájlok).
  • Szempontok szerint választasz csomagot (karbantartás, licenc, biztonság).
  • Biztonságosan frissítesz, és auditálod a függőségeket.

Következő lecke: Publish és terjesztési lehetőségek

A kész program csomagba rendezése: közzétételi profilok, egyetlen fájl, önálló és keretrendszer-függő módok.