📖 Š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).
Rješavanje Konflikata u Migracijama (Best Practice)
Ako 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 jer hash
modeli nisu isti. Pratite ove korake:
- Vratite bazu nazad (Rollback): Izvršite
Update-Database -TargetMigration ImeZadnjeZajednickeMigracijeda obrišete vaš dio iz lokalne baze. - Uklonite vašu lokalnu migraciju: Obrišite konfliktan
.csfajl i uklonite ga iz projekta. - Preuzmite promjene: Primijenite kod svog kolege upotrebom
Update-Database. - Ponovo re-kreirajte: Pozovite
Add-Migration ImeVaseMigracije -Force. Ovo ponovo kreira migraciju tako da se nadovezuje na zadnju ispravnu strukturu bez narušavanja historije.
🌱 Seed Data: Početni Podaci
Seed data su početni podaci koji se ubacuju u bazu kada se primjenjuje migracija. Ovo je korisno za test podatke, admin korisnike, lookup tabele, itd.
Korištenje Seed() Metode
// Migrations/Configuration.cs
protected override void Seed(JavnaUprava.Models.JavnaUpravaDbContext context)
{
// Dodavanje početnih ministarstava
context.Departments.AddOrUpdate(
d => d.DepartmentCode,
new Department { DepartmentName = "Ministarstvo Finansija", DepartmentCode = "MF" },
new Department { DepartmentName = "Ministarstvo Zdravlja", DepartmentCode = "MZ" }
);
context.SaveChanges();
}
Primjena Seed Data
# Seed data se automatski primjenjuje kada izvršite Update-Database
PM> Update-Database
💡 AddOrUpdate Metoda
AddOrUpdate provjerava da li entitet već postoji (na osnovu određenog svojstva,
npr. Email). Ako postoji, ažurira ga. Ako ne postoji, dodaje novi.
Ovo osigurava da seed data ne kreira duplikate pri višestrukim izvršavanjima.
🎯 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.