📖 Šta je Entity Framework?
Entity Framework (EF) je Microsoft-ov Object-Relational Mapping (ORM) framework koji omogućava razvojima da rade sa bazom podataka koristeći .NET objekte umjesto direktnog pisanja SQL upita. EF automatski prevodi LINQ upite u SQL i mapira rezultate nazad u objekte.
🔑 Zašto Koristiti Entity Framework?
- Produktivnost: Manje koda - ne morate pisati SQL upite ručno
- Type Safety: Kompajler hvata greške prije runtime-a
- IntelliSense: Automatsko dovršavanje koda u Visual Studiju
- Migracije: Automatsko upravljanje promjenama u bazi podataka
- Cross-Database: Isti kod radi sa SQL Server, MySQL, PostgreSQL, itd.
🏗️ Arhitektura Entity Framework-a
Za dublje razumijevanje, inženjeri moraju poznavati slojeve apstrakcije koje pruža Entity Framework. EF funkcioniše kao prevodilac i sloj apstrakcije između aplikacije (biznis logike) i same relacione baze podataka. To omogućava izolaciju baze od koda, što je izuzetno važno u razvoju kompleksnih platformi kakve čine e-Uprava.
Slika: Slojevi Entity Framework Arhitekture
Ključne Komponente Arhitekture:
- Entity Data Model (EDM): Centralni dio EF-a koji se sastoji od tri modela:
- Conceptual Model: C# klase (Entiteti) na visokom nivou (npr.
Department,Employee). - Storage Model: Reprezentacija fizičke sheme baze podataka (Tabele, Kolone, Ključevi).
- Mapping Model: Pravila koja definišu kako se Conceptual Model mapira na Storage Model.
- Conceptual Model: C# klase (Entiteti) na visokom nivou (npr.
- Object Services (DbContext): Omogućavaju interakciju koda sa EDM-om. One pretvaraju podatke vraćene iz baze u konkretne .NET objekte, prate promjene na tim objektima (Change Tracking - ključno za naknadno spašavanje) i upravljaju učitavanjem relacija.
- ADO.NET Data Provider: Prevodi generisani Query/Command u SQL specifičan za korištenu bazu (npr. T-SQL za MS SQL Server) i izvršava ga direktno komunicirajući s bazom kroz običnu ADO.NET konekciju.
📦 Entity Framework Verzije
U ovom tutorijalu koristimo Entity Framework 6, posljednju verziju za .NET Framework. Postoji i EF Core za .NET Core/.NET 5+, ali to ćemo pokriti u Modulu 9.
| Verzija | Platforma | Status |
|---|---|---|
| EF 6.x | .NET Framework 4.x | ✅ Maintenance mode |
| EF Core | .NET Core/.NET 5+ | ✅ Aktivno razvijanje |
🔄 Database-First vs Code-First
Entity Framework podržava tri pristupa razvoju: Database-First, Code-First, i Model-First. U praksi se najčešće koriste Database-First i Code-First.
1. Database-First Pristup
Database-First znači da već imate bazu podataka i želite generisati C# klase iz nje. EF kreira Model klase i DbContext automatski na osnovu postojeće baze.
💡 Kada Koristiti Database-First?
- Kada već imate postojeću bazu podataka
- Kada DBA (Database Administrator) upravlja bazom
- Kada radite sa legacy sistemima
- Kada preferirate da baza definiše strukturu
Detaljni Koraci za Database-First (Visual Studio)
Database-First pristup podrazumijeva da EF automatski generiše C# klase na osnovu postojeće baze. Evo kompletne procedure:
KORAK 1: Dodavanje ADO.NET Entity Data Model-a
═══════════════════════════════════════════════
1. Desni klik na projekat (u Solution Explorer)
2. Add → New Item...
3. U prozoru "Add New Item":
- Odaberite kategoriju: Data
- Odaberite template: ADO.NET Entity Data Model
- Name: StateStatisticsModel.edmx
- Kliknite "Add"
KORAK 2: Izbor Pristupa (Entity Data Model Wizard)
═══════════════════════════════════════════════════
U "Choose Model Contents" dijalogu:
├─○ EF Designer from database ← ODABERITE OVO za Database-First
├─○ Empty EF Designer model
├─○ Empty Code First model
└─○ Code First from database
Kliknite "Next"
KORAK 3: Konekcija na Bazu
═══════════════════════════
U "Choose Your Data Connection" dijalogu:
A) AKO NEMATE POSTOJEĆU KONEKCIJU:
1. Kliknite "New Connection..."
2. U "Choose Data Source":
- Data source: Microsoft SQL Server
- Data provider: .NET Framework Data Provider for SQL Server
- Kliknite "Continue"
3. U "Connection Properties":
- Server name: (LocalDb)\MSSQLLocalDB // za LocalDB
- ili: .\SQLEXPRESS // za SQL Express
- ili: sql-server.vlada.local // za production
- Authentication: Windows Authentication // PREPORUČENO
- Select database: StateStatisticsDB
- Kliknite "Test Connection" - treba pokazati "Test connection succeeded."
- Kliknite "OK"
B) AKO IMATE POSTOJEĆU KONEKCIJU:
1. Odaberite iz dropdown liste
2. Provjerite "Save connection settings in Web.config as:"
- Ime: StateStatisticsDBEntities (EF dodaje "Entities" sufiks)
Kliknite "Next"
KORAK 4: Izbor EF Verzije
══════════════════════════
U "Choose Your Version" dijalogu:
- Odaberite: Entity Framework 6.x
Kliknite "Next"
KORAK 5: Izbor Database Objekata
═════════════════════════════════
U "Choose Your Database Objects and Settings" dijalogu:
✅ Tables
└─ ✅ Stats (schema)
├─ ✅ Departments
├─ ✅ Employees
├─ ✅ Managers
├─ ✅ Projects
└─ ✅ ProjectEmployees
□ Views (opciono - možete odabrati)
□ Stored Procedures and Functions (opciono)
Opcije (VAŽNO):
✅ Pluralize or singularize generated object names
(Department klasa = Departments tabela, automatski)
✅ Include foreign key columns in the model
(Uključuje DepartmentID u Employee klasu)
□ Import selected stored procedures and functions into the entity model
Model Namespace: StateStatisticsModel
Kliknite "Finish"
KORAK 6: Generisanje
═════════════════════
Visual Studio će:
1. Kreirati .edmx fajl
2. Generisati sve Model klase
3. Generisati DbContext klasu
4. Dodati connection string u Web.config
5. Dodati EntityFramework NuGet package (ako već nije)
⏳ Proces traje 5-30 sekundi zavisno od veličine baze.
Generisani Fajlovi - Detaljna Analiza
Nakon što wizard završi, Visual Studio generiše nekoliko fajlova. Evo šta svaki radi:
📂 Struktura Generisanih Fajlova
| Fajl | Svrha | Editable? |
|---|---|---|
StateStatisticsModel.edmx |
XML fajl sa metadata o bazi (tabele, relacije, mapiranja) | ⚠️ Rijetko |
StateStatisticsModel.edmx.diagram |
Vizualni dijagram (double-click na .edmx da vidite) | ✅ Da (vizualno) |
StateStatisticsModel.Context.tt |
T4 Template za generisanje DbContext klase | ❌ Ne |
StateStatisticsModel.Context.cs |
DbContext klasa (auto-generated iz .tt) | ⚠️ Izgubićete promjene kod regenerisanja |
StateStatisticsModel.tt |
T4 Template za generisanje Model klasa | ❌ Ne |
Department.cs, Employee.cs, ... |
Model klase za svaku tabelu | ⚠️ Partial classes - možete dodati kroz partial |
⚠️ VAŽNO: Database-First Ograničenja
- 🚫 NE EDITUJTE auto-generisane klase direktno - promjene će biti izgubljene kod regenerisanja
- ✅ Koristite partial classes za dodavanje custom logike:
// CustomDepartment.cs (vaš fajl) public partial class Department { public string DisplayName => $"{DepartmentCode} - {DepartmentName}"; } - ✅ Kada se baza promijeni: desni klik na .edmx → "Update Model from Database"
Primjer Auto-Generisanog DbContext-a
// --------------------------------------------------
// AUTO-GENERATED - NE EDITUJTE DIREKTNO!
// Regeneriše se svaki put kad update-ujete model iz baze
// --------------------------------------------------
namespace JavnaUprava.Models
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
// Napomena: Ime klase dolazi iz "Model Namespace" u wizard-u
public partial class StateStatisticsDBEntities : DbContext
{
// Konstruktor - koristi connection string name iz Web.config
// "name=StateStatisticsDBEntities" znači traži u Web.config
public StateStatisticsDBEntities()
: base("name=StateStatisticsDBEntities")
{
}
// DbContext konfigurisanje konekcije
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// EF automatski poziva ovu metodu pri inicijalizaciji
// Database-First OBIČNO ne dodaje ništa ovdje jer sve čita iz .edmx fajla
throw new UnintentionalCodeFirstException();
}
// DbSet za svaku tabelu koju ste odabrali u wizard-u
// EF pluralizuje imena: Departments (tabela) → Departments (DbSet)
public virtual DbSet<Department> Departments { get; set; }
public virtual DbSet<Employee> Employees { get; set; }
public virtual DbSet<Manager> Managers { get; set; }
public virtual DbSet<Project> Projects { get; set; }
// Junction tabela - EF automatski kreira many-to-many relaciju
public virtual DbSet<ProjectEmployee> ProjectEmployees { get; set; }
}
}
Primjer Auto-Generisane Model Klase
// --------------------------------------------------
// AUTO-GENERATED IZ BAZE: StateStatisticsDB.Stats.Departments
// --------------------------------------------------
namespace JavnaUprava.Models
{
using System;
using System.Collections.Generic;
// partial omogućava vam da dodajete custom kod u zasebnom fajlu
public partial class Department
{
// Konstruktor - EF kreira praznu kolekciju za navigation properties
// Ovo sprječava NullReferenceException kada pristupate dept.Employees
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Department()
{
this.Employees = new HashSet<Employee>();
this.Managers = new HashSet<Manager>();
}
// Primary key - EF detektuje iz baze (IDENTITY column)
public int DepartmentID { get; set; }
// NOT NULL columns postaju required properties
public string DepartmentName { get; set; }
// NULLABLE columns postaju nullable properties
public string DepartmentCode { get; set; }
// decimal(18, 2) u SQL → decimal u C#
public decimal Budget { get; set; }
// Navigation Properties - EF generiše na osnovu Foreign Key relacija
// virtual omogućava Lazy Loading (učitavanje po potrebi)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Employee> Employees { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Manager> Managers { get; set; }
}
}
💡 Zašto 'virtual' Navigation Properties?
Keyword virtual omogućava EF-u da kreira proxy klase za:
- Lazy Loading: Povezani podaci se učitavaju tek kada im pristupite:
var dept = db.Departments.Find(1); // Employees se NE učitavaju odmah foreach (var emp in dept.Employees) // ← SADA se učitavaju iz baze { Console.WriteLine(emp.FirstName); } - Change Tracking: EF prati promjene za optimističko concurrency kontrolu
Bez virtual, morate koristiti Eager Loading:
var dept = db.Departments.Include(d => d.Employees).FirstOrDefault();
2. Code-First Pristup
Code-First znači da pišete C# klase prvo, a EF kreira bazu podataka na osnovu tih klasa. Ovo je moderniji pristup koji daje više kontrole razvojima.
🔑 Kada Koristiti Code-First?
- Kada kreirate novu aplikaciju od nule
- Kada želite potpunu kontrolu nad kodom
- Kada koristite Migracije za upravljanje promjenama
- Kada preferirate da kod definiše strukturu baze
💡 Code-First je Preporučen Pristup
Za nove projekte, Code-First je preporučen jer:
- Omogućava version control nad strukturom baze (kroz Migracije)
- Lakše za refactoring
- Bolja integracija sa development workflow-om
- Podržava TDD (Test-Driven Development)
🏗️ DbContext: Most između Koda i Baze
DbContext je ključna klasa u Entity Framework-u. On predstavlja sesiju sa bazom podataka i omogućava vam da:
- Upravljate entitetima (dodavanje, brisanje, ažuriranje)
- Izvršavate upite (LINQ to Entities)
- Pratite promjene entiteta
- Čuvate promjene u bazu podataka
Kreiranje DbContext Klase (Code-First)
// Models/JavnaUpravaDbContext.cs
using System.Data.Entity;
namespace JavnaUprava.Models
{
public class JavnaUpravaDbContext : DbContext
{
// Konstruktor - prima connection string ime iz Web.config
public JavnaUpravaDbContext()
: base("StateStatisticsDB")
{
}
// DbSet predstavlja kolekciju entiteta u bazi
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Project> Projects { get; set; }
// OnModelCreating - za konfiguraciju modela
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Konfiguracija Stats sheme (jer koristimo postojeću bazu)
modelBuilder.Entity<Department>().ToTable("Departments", "Stats");
modelBuilder.Entity<Employee>().ToTable("Employees", "Stats");
modelBuilder.Entity<Project>().ToTable("Projects", "Stats");
base.OnModelCreating(modelBuilder);
}
}
}
Connection String u Web.config
🔑 Connection String Ime
Ime connection string-a u Web.config mora odgovarati imenu koje prosljeđujete
u DbContext konstruktoru. U primjeru iznad, koristimo "DefaultConnection".
Model Klase (Entiteti)
Model klase predstavljaju tabele u bazi podataka. Svaka klasa postaje tabela, a svojstva postaju kolone.
// Models/Department.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace JavnaUprava.Models
{
[Table("Departments", Schema = "Stats")]
public class Department
{
[Key]
public int DepartmentID { get; set; }
[Required]
[StringLength(100)]
public string DepartmentName { get; set; }
[StringLength(20)]
public string DepartmentCode { get; set; }
public decimal Budget { get; set; }
// Navigation property - kolekcija zaposlenika
public virtual ICollection<Employee> Employees { get; set; }
}
}
// Models/Employee.cs
namespace JavnaUprava.Models
{
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("Employees", Schema = "Stats")]
public class Employee
{
[Key]
public int EmployeeID { get; set; }
[Required]
[StringLength(50)]
public string FirstName { get; set; }
[Required]
[StringLength(50)]
public string LastName { get; set; }
[Required]
[StringLength(13)]
public string JMBG { get; set; } // Specifično za državnu upravu
// Foreign key
public int DepartmentID { get; set; }
// Navigation property
public virtual Department Department { get; set; }
}
}
💡 Navigation Properties
Navigation properties (npr. Department u Employee klasi)
omogućavaju vam da pristupite povezanim entitetima bez eksplicitnih JOIN-ova. EF automatski
učitava povezane podatke kada ih koristite.
📦 Instalacija Entity Framework 6
Korištenje NuGet Package Manager-a
1. Desni klik na projekat → Manage NuGet Packages
2. Pretražite "EntityFramework"
3. Odaberite "EntityFramework" (verzija 6.4.4 ili novija)
4. Kliknite "Install"
Alternativno, koristite Package Manager Console:
PM> Install-Package EntityFramework
Provjera Instalacije
Nakon instalacije, provjerite da li je Entity Framework pravilno instaliran:
packages.configtreba sadržavati<package id="EntityFramework" ...>Web.configtreba imati<entityFramework>sekciju- U References folderu treba postojati
EntityFrameworkiSystem.Data.Entity
🔗 Detaljan Prikaz Relacija u Code-First Pristupu
Prilikom izgradnje sistema javne uprave, rijetko ćete imati entitete koji su izolovani. Najvažniji dio arhitekture softvera i Code-First dizajna je ispravno definisanje relacija (Relationships).
1. Jedan na Više (One-to-Many)
Ovo je najčešća relacija. Primjer: Jedno Ministarstvo (Department) ima više Zaposlenika (Employee). Strani ključ (Foreign Key) se nalazi u zavisnoj tabeli.
public class Department
{
public int DepartmentID { get; set; }
public string Name { get; set; }
// Parent klasa sadrži ICollection (listu) dječijih entiteta.
// Oznaka VIRTUAL omogućava funkciju "Lazy Loading" (učitavanje na zahtjev).
public virtual ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int EmployeeID { get; set; }
public string FullName { get; set; }
// Child klasa sadrži Foreign Key (opciono ali snažno preporučeno zbog obrade u Views-ima)
public int DepartmentID { get; set; }
// Obavezna referenca na instancu parenta
public virtual Department Department { get; set; }
}
2. Više na Više (Many-to-Many)
Primjer: Zaposlenik (Employee) može raditi na više radnih timova ili Projekata
(Project), i obrnuto. EF automatski kreira takozvanu Junction (spojnu) tabelu u
pozadini bez potrebe kreiranja modela, ako su u obje klase definisane ICollection.
public class Employee
{
public int EmployeeID { get; set; }
// Radnik je angažovan na više projekata istovremeno
public virtual ICollection<Project> Projects { get; set; }
}
public class Project
{
public int ProjectID { get; set; }
public string ProjectName { get; set; }
// Na jednom projektu radi više radnika
public virtual ICollection<Employee> Employees { get; set; }
}
3. Jedan na Jedan (One-to-One)
Koristi se za dijeljenje velikih struktura u manje, obično radi sigurnosnog maskiranja podataka ili
optimizacije. Primjer: Zaposlenik i njegov ZdravstveniKarton
(osjetljivi lični podatak).
Da bi EF mapirao 1:1, Primary Key child tabele mora istovremeno biti i Foreign Key koji pokazuje na
Parent tabelu (oznaka Key i ForeignKey zajedno).
public class Employee
{
public int EmployeeID { get; set; }
// Reference na child komponentu
public virtual HealthRecord HealthRecord { get; set; }
}
public class HealthRecord
{
// Key istovremeno služi i kao PK (u svojoj tabeli) i kao FK prema Employee tabeli
[Key, ForeignKey("Employee")]
public int EmployeeID { get; set; }
public string BloodType { get; set; }
// Reference na parenta
public virtual Employee Employee { get; set; }
}
🎯 Praktična Vježba: Kreiranje Prvog DbContext-a
Zadatak: Kreirajte Model za Registar Zaposlenih
Kreirajte Code-First modele za evidenciju zaposlenih u javnoj upravi.
Zadatak 1: Kreirajte Department klasu (ID, Name, Code, Budget).
Zadatak 2: Kreirajte Employee klasu (ID, FirstName, LastName, JMBG,
DepartmentID).
Zadatak 3: Kreirajte JavnaUpravaDbContext i konfigurišite mapiranje
na Stats shemu.
public class JavnaUpravaDbContext : DbContext
{
public JavnaUpravaDbContext() : base("StateStatisticsDB") { }
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().ToTable("Departments", "Stats");
modelBuilder.Entity<Employee>().ToTable("Employees", "Stats");
}
}
✅ Sljedeći Korak
U Lekciji 2.2 ćemo naučiti kako kreirati bazu podataka iz ovih Model klasa koristeći EF Migracije.
✅ Zaključak
U ovoj lekciji ste naučili:
- ✅ Šta je Entity Framework i zašto ga koristiti
- ✅ Razlike između Database-First i Code-First pristupa
- ✅ Kako kreirati DbContext klasu
- ✅ Kako definirati Model klase sa Navigation Properties
- ✅ Kako konfigurisati Connection String
📚 Sljedeća Lekcija
U Lekciji 2.2 ćemo naučiti kako koristiti EF Migracije za
kreiranje i upravljanje promjenama u bazi podataka. Naučićete komande kao što su
Enable-Migrations, Add-Migration, i Update-Database.