A következő címkéjű bejegyzések mutatása: Entity Framework. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: Entity Framework. Összes bejegyzés megjelenítése

2021. november 22., hétfő

Nagy mennyiségű adat beolvasása SQL adatbázisba EntityFramework segítségével

  

Nagy mennyiségű adat beolvasása könnyen hatékonysági problémába torkolhat. Dotnet keretrendszerbe az adatbázisba írásra és olvasásra használhatunk EntityFramework-öt. Ezt elsősorban egy egyszerű példán mutatnám be. Ehhez a példához egy konzol aplikációt kezdtem el 4.7.2 keretrendszerben. Elsősorban kell az EntityFramework NuGet csomag:



Feladat:

A feladat a következő: Egy txt fájlból írjuk be az adatokat SQL adatbázisba. A fájlba utaknak a nevei és kódja vannak tárolva. A fájl minden sora egy utat ír le, ahol az első tag a név a második a kód ezek egy helyközzel vannak elválasztva.

Megoldás:

Elsősorban létrehoztam egy Model könyvtárat a megoldáson belül és ebben létrehoztam az Utak nevű osztályt. Ez az osztály reprezentálja az adatbázisban a táblát, aminek 3 oszlopa lesz: Id, Nev, Kod.

namespace Beolvasas.Model

{

    [Table("Utak")]

    public class Utak

    {

        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]

        [Key]

        public int Id { getset; }

        public string Nev { getset; }

        public long? Kod { getset; }

 

    }

}

Ezek után létrehoztam a Data könyvtárat a megoldáson belül. Ebben raktam az adatbázis osztályt. Ami tartalmazta az Utak táblát.

namespace Beolvasas.Data

{

    class UtakDbContext : DbContext

    {

        public UtakDbContext()

            base("name=UtakDbContext")

        {

        }

        public DbSet<Utak> Utak { getset; }

    }

}

Amit fontos még megtenni, hogy az App.configba rakjuk bele a connectionStringet.

<connectionStrings>

             <add name="UtakDbContext" connectionString="data source=.;initial catalog=UtakDb;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />

       </connectionStrings>

Végül az adatbázis kezelésére is létrehoztam egy osztályt.

namespace Beolvasas

{

    public class AdatbazisKezeles : IDisposable

    {

        private UtakDbContext _utakdb;

        public AdatbazisKezeles()

        {

            _utakdb = new UtakDbContext();

        }

        public void AdatbazisLetrehozasa()//letrehozza az adatbázist, ha nem létezik

        {

            _utakdb.Database.CreateIfNotExists();

            _utakdb.SaveChanges();

        }

        public void AdatokBetoltese(string fajl)

        {

            string fileName = fajl;

            FileStream fs = File.OpenRead(fileName);

            StreamReader sr = new StreamReader(fs);

            string sor;

            while ((sor = sr.ReadLine()) != null)

            {

                Utak ut = new Utak();

                string[] adatok = sor.Split();

                ut.Nev = adatok[0];

                ut.Kod = long.Parse(adatok[1]);

                _utakdb.Utak.Add(ut);

            }

            _utakdb.SaveChanges();

        }

        public void Dispose()

        {

            if (_utakdb != null)

                _utakdb.Dispose();

        }

    }

}

Ennek az osztálynak az AdatbazisLetrehozasa() nevű eljárás létrehozza az adatbázist, ha nem létezik, az AdatokBetoltese(string fajl) eljárás pedig a paraméterben megadott fájlnak az adatait betölti az adatbázisba.
Végül a főprogram:

namespace Beolvasas

{

    class Program

    {

        static void Main(string[] args)

        {

            AdatbazisKezeles adatb = new AdatbazisKezeles();

            adatb.AdatbazisLetrehozasa();      adatb.AdatokBetoltese(@"C:\Users\LászlóCsernok\source\repos\Beolvasas\Utak.txt");

        }

    }

}

Számunkra az AdatokBetoltese nevű eljárás fontosabb. Ebben az eljárásban soronként olvassa be a program az adatokat a fájlból majd létrehoz egy Utak nevű osztályt, ezt feltölti a megfelelő adatokkal, amit a fájlból olvas be. Egy adatot szépen berak a DbSet<Utak> Utak nevű változóba. Amikor minden soron végig ment akkor az adatbázisba beleírja az adatokat a _utakdb.SaveChanges() metódus hívással.
Ez egy működő megvalósítás kisebb adatokra szépen gyorsan működik, de nagyobb adatoknál már érezhető a lassulás. A legnagyobb problémát _utakdb.Utak.Add(uthívás jelenti, mert ez a legidőigényesebb.

 

Javítás 1:

 

Első próbálkozás az lehet, hogy csak egyszer hívjuk a legköltségesebb műveletet az Add-ot. Ezt úgy érjük el, hogy létrehozunk egy listát, amibe beletesszük a beolvasott adatokat. Majd ez a listát adjuk hozzá. Ezzel jelentős mértékben növelhetjük a kód hatékonyságát.
A módosított kód:

 

public void AdatokBetoltese(string fajl)

        {

            string fileName = fajl;

            FileStream fs = File.OpenRead(fileName);

            StreamReader sr = new StreamReader(fs);

            string sor;

            List<Utak> utak = new List<Utak>();// a lista amikbe az elemeket rakjuk

            while ((sor = sr.ReadLine()) != null)

            {

                Utak ut = new Utak();

                string[] adatok = sor.Split();

                ut.Nev = adatok[0];

                ut.Kod = long.Parse(adatok[1]);

                utak.Add(ut);  //hozzá adás

                //_utakdb.Utak.Add(ut); -- régi megvalósítás

            }

            _utakdb.Utak.AddRange(utak);// hozzá adjuk a dbSethez

            _utakdb.SaveChanges();

        }

 

Javítás 2:


Ha még szeretnénk gyorsítani a kódon akkor használhatunk egy NuGet csomagot én az alábbit használtam:



A kód az előbbihez nagyon hasonló marad, csak a _utakdb.Utak.AddRange(utak) sort kell            _utakdb.BulkInsert(utak) -ra kicserélni.

public void AdatokBetoltese(string fajl)

        {

            string fileName = fajl;

            FileStream fs = File.OpenRead(fileName);

            StreamReader sr = new StreamReader(fs);

            string sor;

            List<Utak> utak = new List<Utak>();// a lista amikbe az elemeket rakjuk

            while ((sor = sr.ReadLine()) != null)

            {

                Utak ut = new Utak();

                string[] adatok = sor.Split();

                ut.Nev = adatok[0];

                ut.Kod = long.Parse(adatok[1]);

                utak.Add(ut);  //hozzá adás

                //_utakdb.Utak.Add(ut); -- régi megvalósítás

            }

 

            //_utakdb.Utak.AddRange(utak);// hozzá adjuk a dbSethez -- helyett

            _utakdb.BulkInsert(utak);

            _utakdb.SaveChanges();

        }

Szerintem ezzel a módszer elég gyors adatbetöltés érhető el. Esetleg baj lehet, ha sok az adat és nem fér el a listában, ekkor szerintem darabolással megoldható.

Másik példa: https://dotnetfiddle.net/awlJdf

2016. június 28., kedd

Entity Frameworkkel UDT paraméterű tárolt eljárás futtatása

Az Entity Framework alaphangon nem támogatja a saját SQL típust, vagyis a User Defined Type-ot. Ha egy olyan tárolt eljárást szeretnénk importálni az EF-fel, ami UDT típusú paramétert vár, akkor ugyan nem fog hibát dobni, de nem is fogja legenerálni a hozzátartozó kódot.

Ennek áthidalására egy jó módszer az EntityFrameworkExtras nevű NuGettel is elérhető csomag, aminek segítségével típusosan lehet ilyen tárolt eljárást futtatni. A bekötéséhez ezen a linken van részletesebb leírás.

2011. december 16., péntek

ORA-00932: ellentmondásos adattípusok: - helyett NCLOB szerepel

A címben leírt hibaüzenetet produkáló környezet tömören:

  • IIS7
  • Oracle 11g
  • Entity Framework - Oracle provider Beta

A hibaüzenet nem mond sokat. Az történt, hogy a fejlesztői környezetben minden faszán ment, a teszt környezetben ügyfélnél jött elő ez. Alkalmazás elindulásakor, amikor böngészőben lehívunk egy oldalt (Unity resolution failed exception).
Filóztunk, hogy mi lehet más ott mint itt. Az ottani séma db scriptjéből a fejlesztőin létrehoztam egy újat, hogy lássuk séma eltérés van-e.
Nem. Itt elindult minden.
Karakterkódolás volt a következő tipp, mert amúgy minden stimmelt.
Nem az a para.
Hosszas olvasgatás után kiderült, hogy akkor dob ilyen hibát, amikor nullt kap az EF beta provider egy olyan select-nél paraméterül, amit ellenőrizve a where-ben a mező nem lehet null.
Aztán tipp volt, hogy a usert nem tudja feloldani, és az authentikáció és belépés során itt szállhat el.
Beigazolódott, az IIS-ben elmaradt az anonymus authentikáció tiltása, és null-t küldött a user nevet paraméterül megkapó sql-ben.

Feladat megoldva, de az oracle és a Beta EF provider elmehet a @@@@@ba, hogy ilyen hibaüzenetet dob.

2011. augusztus 15., hétfő

Entity Framework

Ha egy picit okosodni akarsz EF-ből:
https://skydrive.live.com/?cid=245ed00edb4c374e&id=245ED00EDB4C374E%21132

Hátha van olyan, amit még nem láttál. :)

2011. július 29., péntek

DateTime.Kind SQL-ből EF-fel felolvasott és jQuery-vel használt dátumoknál

DB-ben nem tárolódik a DateTime KIND értéke (Local, Utc, Unspecified). a jQuery viszont mindig normalizál UTC-re, így egy local stílusban tárolt DateTime megjárva a UI-t el fog mozdulni 2 órával UTC +2H esetén. Mivel az SQL-be nincs lenyomva ez az érték, ezért normalizálni kell UTC-re, hogy egy teljes kör megtétele után ne másszon el az érték:

Megoldás a Entity Framework-nél:
http://www.aaroncoleman.net/category/Database.aspx

2010. július 15., csütörtök

Entity Framework

Amikor where vagy egyéb extensiont futtatsz egy context-re, akkor az az SQL-en fog lefutni, és nem veszi figyelembe a memóriában lévő módosított entításokat. Ehhez más logika kell:
http://stackoverflow.com/questions/1040532/query-entity-framework-including-records-that-have-not-been-committed

Lényeg, hogy a ObjectStateManager.GetObjectStateEntries kell használni.

2010. május 19., szerda

conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value

Entity Framework SaveChanges() metódus.
Ilyen exception szöveg esetén gyanakodj arra, hogy valamelyik entitás property-je DateTime típusú és nem adtál neki értéket, így a 0001/01/01-et próbálja SQL-be letenni, ami viszont nem megy neki (csak DateTime2-be menne).
Tehát nem a konvertálással van alapvetően a gond, hanem azzal, hogy a DateTime kezdeti értéke nem nyomható le SQL-be.

http://stackoverflow.com/questions/1331779/c-conversion-of-a-datetime2-data-type-to-a-datetime-data-type

2010. január 28., csütörtök

Entity Framework hasznos infók

Találtam/gyűjtöttem pár jó könyvet neten, ami arról szól, hogy hogyan is álljunk neki EF-el fejleszteni, és hogy hogyan kell használni. Sokat nem írok róla, pár perc alatt lejön és a tartalomjegyzékből látszik a lényeg. Sajnos kevés szó esik arról (a tartalomjegyzékből ítélve, még nem olvastam végig őket), hogy hogyan célszerű használni az EF-t 3rétegű architektúrában, ezügyben még keresgélek.

1. Entity Framework learning guide (Janótól)

2. Programming Entity Framework

3. ADO.NET 3.5 with LINQ and the Entity Framework

Blog bejegyzés arról, hogyan kell szétvágni az entity model-t, ha sok entitásunk (db táblánk) van:
Working with Large Databases in Entity Framework

Többrétegű architektúráról is olvastam, hogy hogyan használják. Logikus amit olvastam, de egyelőre csak olyan eseteket, amikor a BL és a UI között egy WCF service réteg is van. Ebben az esetben úgy működik a dolog, hogy ugyebár a BL-ben vannak a metódusok, amivel hívjuk a DAL-t, vagyis be van referálva, és ott ismerjük az entitásokat is, és magát az ObjectContext-et is. Namármost ha a BL kódban (managerben) implementált metódust (ami pl visszaad egy product lsitát) kivezetjük egy WCF service-re, és megjelöljük a datacontract meg operationcontract attribútumokat, akkor amikor a proxyt generáljuk (svcutil) akkor a kliensen meglesznek az entitásaink a proxy generálás után. Tehát az entitások megvannak, a többi, ami az ObjectContext-ben van, annak hívható metódusai stb nem. Tehát pont az van ami kell.

Bővebben itt:
http://msdn.microsoft.com/en-us/magazine/cc700340.aspx
Hasonló cikk, többrétegűség + EF:
https://community.dynamics.com/blogs/cesardalatorre/comments/9584.aspx

További hasznos cikkek:
Data Access Objects with the Entity Framework
Managing Entity Framework ObjectContext lifespan and scope in n-layered ASP.NET applications

És újabb olvasgatásaim után lehet hogy megtaláltam a kulcsszót:
REPOSITORY PATTERN
!!!

Tulajdonságai:
"Testability. Using the pattern we can create stubs that can replace the real data access objects. This can help us to test our business logic without concerning what the data access is doing.
Abstraction. Using the pattern we create an abstraction above our data access functionality. This abstraction can help us when we want to change the implementation of the data access without affecting our business logic code. For example, I had to change implementation of data access with a call to a web service. Using the pattern I only needed to change the object that I used and that is it.
Dependency Injection
. Using the pattern we can use DI containers to inject the relevant object that we want to use in the code."

Azt hiszem ebben az irányban kellene keresgélni, repository-kat kell csinálni és azon keresztül érni el a domain object-eket. Még nem tudom hogyan kell, az a holnap (vagyis már ma) titka.