Adatvezérelt rendszerek¶

- adatelérési könyvtár
- adatbázis használat absztrakció (adatbázis elérése, egységes adatbázisfüggetlen kódolás)
- elemei: Connection, Command, ResultSet, Exception
ADO.NET¶
- adatelérési könyvtár .NET-hez
- interfészeken, absztrakt osztályokon keresztül tudunk így az adatbázissal kommunikálni
- konkrét implementációk Oracle, MySQL, stb szerverekhez
Kapcsolat felépítése¶
-
IDbConnection interfész
- Open, Close, BeginTransaction
-
nyitás és zárás költséges folyamat
-
Connection pooling → cach-elt kapcsolatok (újra felhasználhatóak)
- Connection leak → nehezen kinyomozható
-
Connection String
-
DB szervertől függ a szintaktika
-
sokféle paramétert kell megadni
-
connectionstrings.com
-
Connection string alapú támadások → ConnectionStringBuilder-t használunk!
var builder = new SqlConnectionStringBuilder(); builder.UserID = "User"; builder.Password = "Pw"; builder.DataSpurce = "database.server.hu"; /*builder...*/ var con = new SqlConnection(builder.ConnectionString); con.Open(); //... con.Close();- külön Connection-ok vannak
SqlConnectionaz MSSQL-hez való Connection
- külön Connection-ok vannak
-
-
IDbCommand interfész
- 3 különböző típus (CommandType)
- Tárolt eljárás, Tábla teljes tartalma, SQL query
- parancs szövege (CommandText)
- adatbázis kapcsolat (Connection)
- tranzakció (Transaction)
- timeout (CommandTimeout) → alapértelmezett 30sec
- paraméterek → SQL injection-ra figyelni kell!
- szószerint nem szabad a felhasználótól kapott string-et futtatini SQL parancsként
- 3 különböző típus (CommandType)
-
Parancs végrehajtása
- ExecuteReader: több rekord lekérése
- ExecuteScalar: skalár érték lekérése
- ExecuteNonQuery: eredményhalmaz nélküli parancs (pl: INSERT), érintett sorok számával tér vissza
- ExecuteXmlReader: XML-ként olvassa ki az adatot
- Command.Prepare() → többször egymás után futtatott parancsnál hasznos, szerver oldalon előkészíti a futást
Tranzakciók használata¶
- BeginTransaction: tranzakció létrehozása, izolációs szint itt adható meg
- Transaction tulajdonság: parancs tranzakcióhoz rendelése
- CommitTransaction, RollbackTransaction: tranzakció befejezése
- van általános .NET tranzakciókezelés is (TransactionScope)
- egy tranzakció → max 10perc (és ez NEM megváltoztatható)
- 1 tranzakció 1 connection-höz tartozhat! (különben MSDTC-s hibaüzenet)
- null érték → DBNull.Value, nem pedig a c#-os null (ADO.NET-nél)
Hibakezelés¶
- try-finally block, vagy using (dispose tervezési minta) használata kell!
- reader-t és a kapcsolatot le kell zárni!
DataReader vagy DataSet¶
-
DataReader, azaz kapcsolat alapú modell
-
feldolgozás lépései: kapcsolat megnyitása, parancs futtatása, eredmény feldolgozása, reader lezárása, kapcsolat lezárása
-
általában ezt használjuk, főleg szerveroldalinál
-
kód:
using (var conn=new SqlConnection(connectionString)) { var command = new SqlCommand("SELECT ID, NAME FROM Product", conn); connection.Open(); using(var reader = command.ExecuteReader()) { while (reader.Read()) { Console.WriteLine("{0}\t{1}", reader["ID"], reader["Name"]); } } } -
visszatérni object tér vissza (nem típusos)
-
mikor használjuk?
- pl mikor rövid idejű és folyamatos adatbázis kapcsolat kell a szerverrel, pl webalkalmazások
-
előnyök: egyszerűbb konkurencia kezelés, mindenhol a legfrissebbek az adatok, kisebb memória igény
-
hátrányok: folyamatos hálózati kapcsolat, rossz skálázhatóság
-
-
DataSet, azaz kapcsolat néküli modell
-
feldolgozás lépései: a kapcsolat megnyitása, a DataSet feltöltése, a kapcsolat lezárása, a DataSet feldolgozása, a kapcsolat megnyitása, változtatások visszatöltése, kapcsolat lezárása
-
használat: memóriában módosítgatom csak a dolgokat, majd egyben írom vissza
-
kód:
var dataSet = new DataSet(); var adapter = new SqlDataAdapter(); using (var conn = new SqlConnection(connectionString)) { adapter.SelectCommand = new SqlCommand ("SELECT * FROM Product", conn); connection.Open(); adapter.Fill(dataSet); } //--------------------------------------------------------- foreach(var row in dataSet.Tables[„Product"].Rows) Console.WriteLine("{0}\t{1}", row["ID"], row["Name"]); //--------------------------------------------------------- using (var conn= new SqlConnection(connectionString)){ connection.Open(); adapter.Update(dataSet); // dataSet.AcceptChanges(); // csak az adapter táblája frissül, nem kerül adatbázisba } -
mikor használjuk?
- pl mikor a kapcsolatot csak az adatmanipuláció idejére akarjuk fentartani pl vastag kliens
-
előnyök: nem szükséges folyamatos hálózati kapcsolat, jó skálázhatóság
-
hátrányok: az adatok nem mindig a legfrissebbek, ütközés lehetséges, kliens memóriát foglal
-
Entity Framework¶
-
magasabb absztrakciós szint
-
alapprobléma
- adat és objektum nem egyenlőek → ORM
- SQL → adatorientált és egyszerű lekérdezéseket megfogalmazni
- DE nem típusos, nem objektum alapú, nem épül be nyelvi elemként (String lesz)
- ADO.NET → hatékony, DE nem típusos
-
adatelérés LINQ segítségével
-
példa
from product in db.Products where product.Name == "Lego" select product; -
előnyök: típusos lesz, objektumokra épül, típusellenőrzés fordítási időben
-
-
Entity Framework (EF)
- ORM rendszer
- lehetővé teszi logikai (adatbázis) és fogalmi (üzleti logika) modellek szétválasztását
- függetleníti az adatbázisunkat az adatbázismotortól
-
EDM
-
Entity Data Model
- absztrakciós réteg az adatbázis fülött (O/R mapping modellje), adatbázis motor független
-
2 féle módon létrehozható a mapping
- EDMX fájl (csak EF-ben) → EDM designer
- EDMX nélkül, kódból leírás → runtime EDM
-
csak EF-en:
- database first → van egy adatbázis, és abból generálódik az EDM
-
EF és EF Core is:
-
code first → kódból (c#) generálódik az adatbázis és az EDM is
-
verziókezelésnél ez macerás pl
-
fluent API-val
class MyDbContext : System.Data.Entity.DbContext { //adatbázist rerezentálja public DbSet<Product> Products { get; set; } //Products tábla public DbSet<Category> Categories { get; set; } //Categories tábla protected override void OnModelCreating(ModelBuilder modelBuilder) { //fluent API modelBuilder.Entity<Product>().Property(b => b.Name).IsRequired(); //Product-nak legyen egy Name property-je, amit közelezően meg kell adni modelBuilder.Entity<Product>().HasOne(p => p.Category).WithMany(c => c.Products); //Product-nak elgyen 1-több kapcsolata a Category-ról a Products-ra } } -
attribútumokkal
[Table("Product")] class Product { [Key] public int Id { get; set; } [Required] [StringLength(1000)] public string Name { get; set; } public VAT VAT { get; set; } public ICollection<Category> Categories { get; set; } }
-
-
-
Navigation property → adatbázis join automatikusan
- nem kell leírni a join-t, mert maga a fluent API-s vagy attribútumos leírás tartalmazza!
-
-
DbContext
-
adatbázis elérés központi osztálya
-
rajta keresztül indítható lekérdezés
-
nyilvántartja az összes entitást és rajtuk végzett módosítást → SaveChanges() menti az adatbázisba
- SaveChanges = tranzakcióba fogja menteni
-
rövid életciklusú → using-ba tegyük!
-
NEM szálbiztos! (mindig újat hozzunk létre!)
-
ne cache-eljünk vele
-
példányosított DbContext = nyitott adatbázis kapcsolat
-
kulcsok
- elsődleges kulcsot konvenció alapján találja meg → Id, ProductId
- más mezőnév használatakor jeleznünk kell azt →
[Key]vagymodelBuilder.Entity<Product>().HashKey(c => c.UniqueName); - összetett kulcs is lehet
- privát kulcs nélküli táblák → nem képezhetők le!
-
lekérdezés
- DbContext
- listát vezet az újonnan felvett és törölt entitásokról
- nyilvántartja az objektumokon történt változtatásokat
- nyilvántartja a lekérdezett entitásokat
- AsNoTracking() → ha csak lekérdezzük, és nem akarjuk módosítani azokat!
- hasznos, mert erőforrást spórolunk vele
- DbContext
-
új entitások beszúrása és mentése
- új entitás létrehozása, kulcs üresen hagyva →
var newEntity = new Class() - DbContext-hez rendelés
DbContext.DbSet.Add(newEntity)- entitáson kerüli hozzákötés →
someEntity.Property = newEntity
DbContext.SaveChanges()- EF lefuttatja az INSERT SQL utasításokat
- elsődleges és külső kulcsok bekerülnek a DbContext-hez tartozó entitásokba
- új entitás létrehozása, kulcs üresen hagyva →
-
entitások módosítása
-
tulajdonság módosítása
-
DbContext nyilvántartja a változtatást
-
SaveChanges() véglegesíti
-
példa
var course = context.Course.Single(q => q.Neptun=="VIAUAC01"); var aut = context.Department.Single(q => q.Code=="AUT"); course.Name = "Adatvezérelt rendszerek"; course.Department = aut; context.SaveChanges();
-
-
entitások törlése
-
csak betöltött entitás törölhető
-
DbSet.Remove(...), FONTOS hogy a zárójelbe NEM adható me LINQ lekérdezés! -
SaveChanges() véglegesíti
-
példa
var c = context.Course.Single(q => q.Neptun=="VIAUAC01"); context.Course.Remove(c); context.SaveChanges();
-
-
-
eager loading / lazy loading
- eager loading
- hivatkozás betöltésének kezdeményezése
context.Products.Include(entity => entity.NavProp)- egy SQL lekérdezésbe hozza az adatokat, de többet nem kell lekérdeznie
- lazy loading
- navigation property-k mentén a hivatkozott entitás betöltése amikor először használjuk
- olcsóbb lekérdezés az elején, DE hivatkozott entitás betöltésekor újabb lekérdezésbe fog ez kerülni
- alapból ez van bekapcsolva
- eager loading
-
entitás típusok
- EF esetén
- EntityObject (csak EF-nél) → ebből fognak származni az osztályok (picit sok felelőssége van!)
- POCO (Plane old CLR object) → egyszerűbb osztály legyen, egy felelősségük van
- változáskövetés nincs benne → összehasonlítgatással megy
- lazy loading támogatás nincs
- POCO Proxy → POCO + változáskövetés és lazy loading
- runtime generálódik a csúnya EntityObject szerű osztály
- ez az alap a rendszerben
- EF esetén
-
tranzakciók
- SaveChanges tranzakcionális
- explitic tranzakció indítása
context.Database.BeginTransaction()
-
tárolt eljárások (csak EF-nél)
- 2 módon:
- DbContext függvényeiként
- Entitások adatmódosító függvényeiként: Insert, Update, Delete
- 2 módon:
EF Core extrák¶
- DbContext.OnConfiguring → NE használjuk ha lehet
- adat konverzió, pl enum -> string
- migráció → C# kódból vezérelt adatbázis séma változtatás
- adatbázis létrehozása C# kódból:
dbContext.Database.EnsureCreated()(ilyet szabad code first modellből!)