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

Nincsenek megjegyzések:

Megjegyzés küldése