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 { get; set; }
public string Nev { get; set; }
public long? Kod { get; set; }
}
}
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 { get; set; }
}
}
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(ut) hí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