Kihagyás

Adatvezérelt rendszerek

  • image-20201031170555367
  • 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 SqlConnection az MSSQL-hez való Connection
  • 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
  • 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] vagy modelBuilder.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
    • ú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
    • 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
  • 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
  • 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

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!)