A következő címkéjű bejegyzések mutatása: adat. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: adat. Ö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