📖 Šta su Migracije?
Entity Framework Migracije omogućavaju vam da upravljate promjenama u strukturi baze podataka kroz kod. Umjesto ručnog pisanja SQL skripti, migracije automatski generišu SQL kod na osnovu promjena u vašim Model klasama.
🔑 Zašto Koristiti Migracije?
- Version Control: Migracije su C# fajlovi - možete ih commit-ovati u Git
- Reproducibilnost: Ista migracija će kreirati istu bazu na svim okruženjima
- Rollback: Možete vratiti promjene ako nešto pođe po zlu
- Timski Rad: Svaki developer može primijeniti iste promjene
- Automatski SQL: Ne morate pisati CREATE TABLE, ALTER TABLE, itd. ručno
🔄 Životni Ciklus Migracija i __MigrationHistory
Da biste razumjeli kako Entity Framework zna koje su migracije primijenjene, potrebno je razumjeti
njegov unutrašnji mehanizam i tabelu __MigrationHistory.
Slika: Životni ciklus EF Migracija
Šta je __MigrationHistory?
Prilikom prvog pokretanja Update-Database, EF kreira sistemsku tabelu pod imenom
__MigrationHistory u vašoj bazi. Njena uloga je esencijalna:
- Prati MigrationId (npr.
20231015120000_InitialCreate) svake izvršene migracije. - Sadrži Model kolonu (baza čuva binarni potpis vaših entiteta) putem koje EF provjerava jesu li klase i baza sinhronizovane.
- Sprečava ponovno pokretanje već izvršenih SQL skripti prilikom svakog poziva
Update-Database.
🚀 Enable-Migrations: Aktiviranje Migracija
Prije nego što možete koristiti migracije, morate ih omogućiti u vašem projektu. Ovo se radi jednom po projektu.
Koraci za Omogućavanje Migracija
-
Otvorite Package Manager Console
- Tools → NuGet Package Manager → Package Manager Console
- Ili View → Other Windows → Package Manager Console
-
Izvršite komandu:
🛠️ Enable-Migrations Komanda
PM> Enable-Migrations -
Provjerite da li je kreiran Migrations folder
- U Solution Explorer-u trebate vidjeti
Migrations/folder - U njemu će biti
Configuration.csfajl
- U Solution Explorer-u trebate vidjeti
Configuration.cs Fajl
Nakon Enable-Migrations, EF kreira Configuration.cs fajl koji sadrži postavke
za migracije.
// Migrations/Configuration.cs
namespace JavnaUprava.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<JavnaUprava.Models.JavnaUpravaDbContext>
{
public Configuration()
{
// Automatske migracije - preporučeno false za produkciju
AutomaticMigrationsEnabled = false;
}
protected override void Seed(JavnaUprava.Models.JavnaUpravaDbContext context)
{
// Seediranje početnih ministarstava
context.Departments.AddOrUpdate(
d => d.DepartmentCode,
new Models.Department { DepartmentName = "Ministarstvo Finansija", DepartmentCode = "MF", Budget = 50000000 },
new Models.Department { DepartmentName = "Ministarstvo Pravde", DepartmentCode = "MP", Budget = 35000000 }
);
// Seediranje administratora
context.Employees.AddOrUpdate(
e => e.JMBG,
new Models.Employee
{
FirstName = "Admin",
LastName = "Adminić",
JMBG = "1234567890123",
DepartmentID = 1 // Vežemo za MF
}
);
}
}
}
💡 AutomaticMigrationsEnabled
AutomaticMigrationsEnabled = false znači da morate eksplicitno kreirati migracije.
Preporučeno je ostaviti na false jer daje više kontrole i omogućava review migracija
prije primjene.
➕ Add-Migration: Kreiranje Migracije
Kada promijenite Model klase (dodate svojstvo, promijenite tip, itd.), morate kreirati novu migraciju koja će zabilježiti te promjene.
Osnovna Sintaksa
PM> Add-Migration MigrationName
# Primjer:
PM> Add-Migration InitialCreate
PM> Add-Migration AddEmailToEmployee
PM> Add-Migration CreatePostsTable
Primjer: Kreiranje Prve Migracije
Pretpostavimo da imate Model klase Employee i Department koje ste kreirali u
prethodnoj lekciji, ali još nema baze podataka.
# 1. Omogućite migracije (ako već niste)
PM> Enable-Migrations
# 2. Kreirajte prvu migraciju koja će kreirati sve tabele
PM> Add-Migration InitialCreate
EF će kreirati novi fajl u Migrations/ folderu sa nazivom kao što je
20240101120000_InitialCreate.cs (timestamp + ime migracije).
Struktura Migracije Fajla
// Migrations/20240101120000_InitialCreate.cs
namespace JavnaUprava.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class InitialCreate : DbMigration
{
public override void Up()
{
// Kreiranje tabela u 'Stats' shemi
CreateTable(
"Stats.Departments",
c => new
{
DepartmentID = c.Int(nullable: false, identity: true),
DepartmentName = c.String(nullable: false, maxLength: 100),
DepartmentCode = c.String(maxLength: 20),
Budget = c.Decimal(nullable: false, precision: 18, scale: 2),
})
.PrimaryKey(t => t.DepartmentID);
CreateTable(
"Stats.Employees",
c => new
{
EmployeeID = c.Int(nullable: false, identity: true),
FirstName = c.String(nullable: false, maxLength: 50),
LastName = c.String(nullable: false, maxLength: 50),
JMBG = c.String(nullable: false, maxLength: 13),
DepartmentID = c.Int(nullable: false),
})
.PrimaryKey(t => t.EmployeeID)
.ForeignKey("Stats.Departments", t => t.DepartmentID, cascadeDelete: true)
.Index(t => t.DepartmentID);
}
public override void Down()
{
DropForeignKey("Stats.Employees", "DepartmentID", "Stats.Departments");
DropIndex("Stats.Employees", new[] { "DepartmentID" });
DropTable("Stats.Employees");
DropTable("Stats.Departments");
}
}
}
🔑 Up() i Down() Metode
- Up(): Primjenjuje promjene (kreira tabele, dodaje kolone, itd.)
- Down(): Vraća promjene (briše tabele, uklanja kolone, itd.)
Down() metoda omogućava rollback - vraćanje na prethodnu verziju baze.
Primjer: Dodavanje Nove Kolone
Pretpostavimo da želite dodati PhoneNumber svojstvo u Employee klasu.
// Models/Employee.cs
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
// Nova kolona
[StringLength(20)]
public string PhoneNumber { get; set; }
public DateTime HireDate { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
}
PM> Add-Migration AddPhoneNumberToEmployee
// Migrations/20240101130000_AddBudgetToDepartment.cs
public partial class AddBudgetToDepartment : DbMigration
{
public override void Up()
{
AddColumn("Stats.Departments", "Budget", c => c.Decimal(nullable: false, precision: 18, scale: 2));
}
public override void Down()
{
DropColumn("Stats.Departments", "Budget");
}
}
💾 Update-Database: Primjena Migracija
Nakon što kreirate migraciju, morate je primijeniti na bazu podataka.
Ovo će izvršiti SQL kod iz Up() metode.
Osnovna Sintaksa
# Primjenjuje sve migracije koje još nisu primijenjene
PM> Update-Database
# Primjenjuje migraciju do određene migracije
PM> Update-Database -TargetMigration MigrationName
# Vraća bazu na određenu migraciju (rollback)
PM> Update-Database -TargetMigration PreviousMigrationName
# Prikazuje SQL koji će biti izvršen (bez primjene)
PM> Update-Database -Script
# Primjenjuje migraciju na određeni connection string
PM> Update-Database -ConnectionString "..." -ConnectionProviderName "System.Data.SqlClient"
Primjer: Primjena Prve Migracije
# 1. Kreirajte migraciju
PM> Add-Migration InitialCreate
# 2. Primijenite migraciju (kreira bazu i tabele)
PM> Update-Database
Nakon Update-Database, EF će:
- Kreirati bazu podataka (ako ne postoji)
- Kreirati tabele na osnovu Model klasa
- Kreirati foreign key veze
- Kreirati indekse
💡 Provjera Rezultata
Nakon Update-Database, možete provjeriti da li je baza kreirana:
- Otvorite Server Explorer u Visual Studiju
- Proširite Data Connections
- Trebate vidjeti vašu bazu sa tabelama
Primjer: Primjena Dodatne Migracije
# 1. Promijenite Model klasu (dodajte PhoneNumber)
# 2. Kreirajte migraciju
PM> Add-Migration AddPhoneNumberToEmployee
# 3. Primijenite migraciju
PM> Update-Database
↩️ Rollback Migracija
Ponekad možete htjeti vratiti promjene u bazi. EF omogućava rollback kroz
Update-Database -TargetMigration komandu.
# Pretpostavimo da imate migracije:
# - InitialCreate
# - AddPhoneNumberToEmployee
# - AddSalaryToEmployee
# Vraćanje na AddPhoneNumberToEmployee (uklanja AddSalaryToEmployee promjene)
PM> Update-Database -TargetMigration AddPhoneNumberToEmployee
# Vraćanje na početak (briše sve tabele)
PM> Update-Database -TargetMigration $InitialDatabase
⚠️ Opasnost Rollback-a
Rollback može obrisati podatke! Ako vratite migraciju koja je dodala kolonu, svi podaci u toj koloni će biti izgubljeni. Uvijek napravite backup prije rollback-a u produkciji.
👥 Timski Rad i Rješavanje Konflikata
Radeći na sistemima u javnoj upravi, često više inženjera radi na istoj bazi na različitim modulima infrastrukture, što dovodi do konflikata u migracijama (npr. dva inženjera naprave i prime različite migracije u isto vrijeme). Ovo se najčešće dešava kada koristite Git za source control.
Rješavanje Konflikata u Migracijama (Best Practice)
Kada povučete kod (git pull) koji sadrži novu migraciju od kolege, a vi ste u
međuvremenu lokalno definisali vlastitu na čekanju, Entity Framework će prijaviti konflikt
(Model-backing-context has changed) jer hash modeli u __MigrationHistory više nisu
isti. Pratite ove korake:
- Vratite bazu nazad (Rollback): Izvršite
Update-Database -TargetMigration ImeZadnjeZajednickeMigracije(to je migracija prije nego ste vi i vaš kolega počeli raditi). Ovo briše vaše promjene iz lokalne baze. - Uklonite vašu lokalnu migraciju: Obrišite konfliktan
.csfajl vaše migracije i uklonite ga iz projekta. Model klase zadržite! - Preuzmite promjene kolege na bazu: Primijenite kod svog kolege upotrebom
Update-Database. Sada vaša baza ima koleginu najnoviju verziju. - Ponovo re-kreirajte: Pozovite
Add-Migration ImeVaseMigracije. Ovo generiše novu, čistu migraciju koja sada u sebi kombinuje i prepoznaje promjene vašeg kolege i dodaje vaše izmjene na vrh kao najnoviji sloj.
🌱 Seed Data: Početni i Testni Podaci
Seed data su podaci koji se programski ubacuju u bazu svaki put kada se pokrene
Update-Database.
Sistemi javne uprave se ne mogu testirati na praznoj bazi. Trebaju vam šifrarnici (katalozi gradova,
zanimanja, statusa), defaultni admin račun, i ogromna količina testnih zapisa kako bi klijent mogao
vršiti prve vizualizacije portala.
Korištenje Seed() Metode
// Migrations/Configuration.cs
protected override void Seed(JavnaUprava.Models.JavnaUpravaDbContext context)
{
// 1. Šifrarnici (Lookup data) pomoću AddOrUpdate
// AddOrUpdate garantuje da nećete duplirati gradove pri višestrukim pokretanjima Seed-a
context.Departments.AddOrUpdate(
d => d.DepartmentCode, // Ključ po kojem pretražuje da li već postoji
new Department { DepartmentName = "Ministarstvo Finansija", DepartmentCode = "MF" },
new Department { DepartmentName = "Ministarstvo Zdravlja", DepartmentCode = "MZ" },
new Department { DepartmentName = "Ministarstvo Unutrašnjih Poslova", DepartmentCode = "MUP" }
);
// Obavezno čuvamo izmjene prije dodavanja djece, kako bi Departments dobili primarne ključeve
context.SaveChanges();
// 2. Kreiranje admin korisnika
context.Employees.AddOrUpdate(
e => e.JMBG,
new Employee { FirstName = "Sistem", LastName = "Administrator", JMBG = "0000000000000", DepartmentID = 1 }
);
// 3. Generisanje velikih količina testnih podataka samo na Development okruženju
#if DEBUG
if (!context.Employees.Any(e => e.FirstName == "TestUser0"))
{
for (int i = 0; i < 100; i++)
{
context.Employees.Add(new Employee
{
FirstName = $"TestUser{i}",
LastName = $"Prezime{i}",
JMBG = $"1111111111{i:000}", // Formatiranje broja sa nulama
DepartmentID = (i % 3) + 1 // Rasporedi u 3 ministarstva
});
}
}
#endif
context.SaveChanges();
}
Primjena Seed Data
# Seed metoda se izvršava AUTOMATSKI nakon primjene zadnje migracije!
PM> Update-Database
💡 Moć AddOrUpdate Metode
AddOrUpdate je takozvana "Upsert" komanda. Ona prvo pretražuje bazu na osnovu
specifičnog svojstva (npr. DepartmentCode iz primjera iznad). Ako zapis sa tim kodom
pronađe (npr "MF"), metoda će izvršiti SQL UPDATE. Ako ga ne pronađe, izvršit će
SQL INSERT.
Ovo je krucijalno, jer se metoda Seed() okida apsolutno svaki put kada bilo ko pozove
Update-Database u Package Manager-u, pa se na ovaj način štitite od kreiranja duplih
"Ministarstvo Finansija" rekorda iznova i iznova.
🎯 Praktična Vježba: Kompletna Migracija
Zadatak: Migracija Javne Uprave
Kreirajte i primijenite migracije za vaš JavnaUpravaDbContext.
Zadatak 1: Omogućite migracije i kreirajte InitialCreate koja
formira Stats.Departments i Stats.Employees tabele.
Zadatak 2: Dodajte IsActive (bool) u Department model,
te kreirajte migraciju AddIsActive.
Zadatak 3: Kreirajte Seed metodu koja puni tabelu sa 3 ministarstva
i primijenite je pomoću Update-Database.
PM> Enable-Migrations
PM> Add-Migration InitialCreate
PM> Update-Database
PM> Add-Migration AddIsActive
PM> Update-Database
✅ Zaključak
U ovoj lekciji ste naučili:
- ✅ Kako omogućiti migracije sa
Enable-Migrations - ✅ Kako kreirati migracije sa
Add-Migration - ✅ Kako primijeniti migracije sa
Update-Database - ✅ Kako rollback-ovati migracije
- ✅ Kako dodati seed data u bazu
📚 Sljedeća Lekcija
U Lekciji 2.3 ćemo naučiti kako izvršavati CRUD operacije koristeći Entity Framework. Naučićete LINQ to Entities, Eager/Lazy/Explicit loading, i kako efikasno raditi sa povezanim podacima.