În calitate de programator, poți lucra la o aplicație complet inovatoare sau la o aplicație nouă, bazată pe o altă aplicație existentă. O aplicație complet nouă este o situație fericită pentru că aduce cu ea o arhitectură nouă și ultimele tehnologii. Pe scurt, aceasta oferă avantajele oricărui start de la zero. Spre deosebire de aceasta, aplicația nouă, bazată pe o altă aplicație existentă, aduce cu ea riscurile unei baze de date deja configurate.
Toți vrem să lucrăm la o aplicație complet nouă, dar cei mai mulți dintre noi lucrează la o aplicație nouă, bazată pe o altă aplicație existentă. Pentru că aplicația existentă este folosită, utilizatorii acestei aplicații își doresc să folosească aplicația nouă cât mai repede.
Cum ar trebui integrată baza de date existentă în aplicația nouă?
Care este cel mai bun mod de lucru pentru a avea o dezvoltare rapidă a noii aplicații? Pentru acest exemplu, baza de date folosită este MS SQL și Entity Framework este sistemul ORM.
Despre migrările "Code First"
Într-o aplicație, modelele de date se adaugă și se schimbă frecvent, ajungând să nu mai fie sincronizate cu baza de date. Cu metoda de lucru "Code First" migrările sunt folosite pentru a păstra sincronizate modelele de date cu baza de date.
Migrările permit crearea unei baze de date inițiale care lucrează cu Entity Framework. Pentru fiecare set de schimbări, o nouă migrare trebuie generată ca să sincronizeze baza de date.
Metoda "Code First" permite să se sincronizeze schema bazei de date în loc să se șteargă și să se recreeze baza de date.
Baza de date există și poate fi folosită.Metoda "Database First" este o alternativă a metodei "Code First", în care se creează clasele POCO din baza de date existentă.
În orice aplicație, în timpul dezvoltării, apar noi cerințe care necesită schimbări în baza de date. Baza de date este modificată manual și modelele de date sunt sincronizate în acord cu modificările din baza de date.
Modelele de date se creează pornind de la tabelele din baza de date relațională (comanda scaffold din Package Manager Console).
Care metodă este mai potrivită pentru a avea o implementare rapidă?
O comparație între metodele "Code First" și "Database First" ne va ajuta să luăm o decizie.
Code First | Database First | |
---|---|---|
New Models | Write them manually in .Net. Migrate to | Scaffold them from database. |
database. | ||
Change Models | Change in .Net. Migrate to database. | Change in database. Scaffold from |
database. | ||
Remove Models | Remove in .Net. Migrate to database. | Remove in database. Remove in .Net. |
History of Models Changes | Yes | No |
Developers use the same database | Migrations resolve the changes. | Developers have the changes. |
Developers use a local database | Migrations resolve the changes. | Every developer should apply the changes |
locally. |
"Database First" poate fi folosită să se obțină modelele de date din baza de date. Când avem modelele de date, "Code First" este recomandată să fie folosită pentru schimbările care apar.
Așadar, în cazul nostru, soluția de mijloc este cea mai bună. Vom folosi ambele metode, "Database First" la început, doar ca să obținem modelele din baza de date și, după aceea, vom continua cu metoda "Code First" pentru schimbările care apar ulterior.
Cum să schimbi modul de lucru "Database First" cu "Code First"?
Nu uita că baza de date există și o nouă aplicație este în dezvoltare.
Ca să nu fie afectată această aplicație, un proiect nou este creat temporar doar ca să obținem modelele de date din baza de date. Folosind metoda "Database First", modelele de date sunt obținute din baza de date.
După ce avem modelele, verificăm dacă acestea generează o bază de date identică. Pentru aceasta vom schimba stringul de conectare la o bază de date nouă.
Acum putem schimba metoda de lucru și să folosim în continuare "Code First" pentru crearea migrării inițiale pentru o nouă bază de date specificată în stringul de conectare. Această migrare inițială s-ar putea să nu conțină toate informațiile pentru a se putea crea noua bază de date. Din acest motiv, s-ar putea să se afișeze erori când se aplică prima migrare și noua bază de date să nu se creeze. Aceste erori trebuie rezolvate schimbând manual prima migrare.
Structura bazei de date vechi și structura bazei de date noi se pot compara, doar ca să ne asigurăm că am obținut ceea ce doream. Dacă totul este așa cum ne-am așteptat, atunci tabela __EFMigrationHistory din noua bază de date trebuie importată în vechea bază de date, ca să putem folosi în continuare, în aplicație, metoda "Code First" pentru schimbările care vor apărea.
Pe urmă, mutăm și modelele de date din proiectul temporar în aplicație unde la dezvoltare s-a folosit metoda "Database First" și continuăm dezvoltarea aplicației folosind metoda "Code First".Un exemplu al modului cum se face această schimbare de la o metodă la alta este descris mai jos.
Creăm un proiect nou .Net Web API și instalăm pachetele următoare din NuGet pentru a putea obține modelele și DBContext din baza de date:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design
Generăm modelele de date folosind metoda "Database First", rulând următoarea comandă în Package Manager Console:
Scaffold-DbContext
-Provider Microsoft.EntityFrameworkCore.SqlServer
-Connection "Data Source=...;
Integrated Security = True; Initial Catalog = ...;"
Modelele sunt generate și apar în proiect.
Setăm stringul de conectare la o bază de date nouă, NU la baza de date existentă.
Modelele generate și contextul bazei de date pot fi mutate in directorul "Models" al proiectului. De asemenea, Startup.cs și Program.cs trebuie să fie actualizate pentru DBContext generat.
Cu metoda "Code First", migrarea inițială numită InitialCreate
trebuie creată din Package Manager Console folosind comanda Add-Migration InitialCreate
.
Ca să sincronizăm baza de date trebuie doar să pornim aplicația .Net sau să rulăm comanda Update-database -Verbose din Package Manager Console.
În funcție de informațiile și setările din baza de date existentă, s-ar putea să se afișeze ceva erori la sincronizare. În pasul următor, trebuie sa rezolvăm toate aceste erori.
Erorile apar când proiectul .Net pornește sau când rulăm comanda update-database -Verbose
din Package Manager Console.
Exemplu de astfel de erori care pot să apară este că unele câmpuri din SQL folosesc o funcție definită în SQL, și această funcție nu există în baza de date nouă care trebuie creată. Ca să rezolvăm această eroare, funcția SQL care lipsește, trebuie să o adăugăm manual în migrarea inițială.
Un alt exemplu de eroare este că, dacă sunt definiți utilizatori și roluri în SQL, fără drepturile necesare, unele tabele nu se pot crea. Ca să rezolvăm această eroare, utilizatorii și rolurile trebuie adăugate manual în migrarea inițială.
După ce s-au rezolvat toate erorile și noua bază de date a fost generată, putem trece la pasul următor.
Ca să fim siguri că modelele obținute din baza de date existentă generează o bază de date similară, folosind migrarea InitialCreate
, trebuie să comparăm structura celor două baze de date.
Pentru că în exemplu s-a folosit bază de date MS SQL, din SQL Server Management Studio, generăm scripturi cu toate informațiile din baza de date nouă. Aceleași scripturi le generăm și din baza de date existentă.
Eu am folosit Total Commander/Compare by content, dar orice comparator text poate fi folosit pentru compararea acestor scripturi. Acum am comparat doar structura bazelor de date nu și datele din bazele de date.
Observație: Unele diferențe la unii indecși sunt acceptate, dar tabelele, cheile primare și cheile străine trebuie să fie la fel.
Oricum vom folosi baza de date existentă și acolo vom avea toate informațiile.
__EFMigrationsHistory
Până acum, cele două baze de date par să fie la fel.
Tabela __EFMigrationsHistory
cu migrarea InitialCreate
din baza de date nouă trebuie să o importăm în baza de date existentă. Pentru acest exemplu, SQL Server Management Studio a fost folosit și la importarea tabelei.
Pentru că vom folosi baza de date existentă de acum înainte, stringul de conectare trebuie să aibă valoarea potrivită.
Daca rulăm aplicația, atunci nici o schimbare nu se va face în baza de date existentă. De asemenea, dacă nu schimbăm nici un model și adăugăm o migrare nouă, această migrare va fi goală. Baza de date este sincronizată cu modelele de date.
Când, de exemplu, se modifică un model, se șterge o proprietate, în migrare trebuie incluse modificările manuale din views, store procedures, functions, triggers etc.
Vechea bază de date nu a fost modificată pe parcursul acestui exemplu. Modelul "Code First" poate fi folosit în proiect pentru viitoarele modificări ale modelelor după migrarea inițială.
Acești pași pot fi făcuți chiar dacă implementarea noii aplicații este începută și programatorii au lucrat cu modelul "Database First" până acum. Modelele de date, migrarea InitialCreate pot fi copiate în noua aplicație și se poate continua implementarea folosind modelul "Code First".
Pentru alte sisteme ORM care suportă "Code First" și "Database First", aceeași strategie poate fi folosită pentru a schimba modelul de lucru "Database First" cu "Code First".
Sunt și alte exemple cum să folosești "Code First Migrations" cu o bază de date existentă.
de Ovidiu Mățan
de Mihai Talpoș