DAW Laborator 04 - EF Core Razor Pages

CTI – Dezvoltarea Aplicațiilor Web – Laborator 4

Entity Framework Core + Razor Pages – Proiect: News Portal

Obiective

În acest laborator vom construi baza aplicației care va fi folosită pe parcursul semestrului.

Aplicația va fi un portal simplu de știri (News Portal), implementat cu Razor Pages și Entity Framework Core.

Obiectivele laboratorului:

Tehnologii utilizate

Crearea proiectului

Creați un proiect nou de tip ASP.NET Core Web App (Razor Pages).

Selectați .NET 8. Rulați aplicația pentru a verifica că funcționează.

Instalarea pachetelor NuGet

Instalați următoarele pachete:

Aceste pachete permit conectarea la baza de date, generarea migrărilor, și executarea comenzilor EF.

Structura proiectului

Adăugați următoarele foldere și fișiere:

Models/
    Article.cs
    Category.cs
Data/
    AppDbContext.cs
Pages/
    Articles/
        Index.cshtml
        Index.cshtml.cs

Modelele aplicației

Vom avea două tabele: Categories și Articles. O categorie poate avea mai multe articole.

Modelul Category

using System.ComponentModel.DataAnnotations;

public class Category
{
    public int Id { get; set; }

    [Required]
    [MinLength(2)]
    public string Name { get; set; } = string.Empty;

    public List<Article> Articles { get; set; } = [];
}

Modelul Article

using System.ComponentModel.DataAnnotations;

public class Article
{
    public int Id { get; set; }

    [Required]
    [MinLength(5)]
    public string Title { get; set; } = string.Empty;

    [Required]
    [MinLength(20)]
    public string Content { get; set; } = string.Empty;

    public DateTime PublishedAt { get; set; } = DateTime.Now;

    public int CategoryId { get; set; }

    public Category Category { get; set; }  = null!;
}

DbContext

DbContext reprezintă conexiunea dintre aplicație și baza de date. Este clasa principală prin care interacționăm cu baza de date în Entity Framework Core.

Adăugați fișierul Data/AppDbContext.cs:

using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }

    public DbSet<Article> Articles { get; set; }

    public DbSet<Category> Categories { get; set; }
}

Connection String

În fișierul appsettings.json adăugați un câmp pentru connection string:

"ConnectionStrings": {
  "Lab04": ""
}

Deschideți SQL Server Object Explorer (View → SQL Server Object Explorer) și selectați instanța (localdb)\MSSQLLocalDB. Click dreapta → Properties și copiați valoarea Connection String.

Lipiți valoarea copiată în câmpul "Lab04" din appsettings.json. Apoi setați valoarea de la Initial Catalog la NewsDb.

Rezultatul final ar trebui să arate astfel:

"ConnectionStrings": {
  "Lab04": "Server=(localdb)\\MSSQLLocalDB;Database=NewsDb;Trusted_Connection=True;"
}

Configurarea DbContext

În Program.cs adăugați, după builder.Services.AddRazorPages():

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Lab04")));

Această linie înregistrează AppDbContext în containerul de Dependency Injection. Framework-ul va ști cum să creeze instanțe ale contextului oriunde este cerut.

Migrations

Migrations sunt mecanismul prin care EF Core traduce modelele C# în structura bazei de date. La fiecare modificare a modelelor, creăm o nouă migrare care descrie schimbările.

În Package Manager Console (Tools → NuGet Package Manager → Package Manager Console):

Add-Migration InitialCreate
Update-Database

Introducerea datelor inițiale

Înainte de a continua, vom adăuga manual câteva înregistrări pentru a avea date de test.

Deschideți SQL Server Object Explorer → expandați baza de date NewsDb → Tables. Click dreapta pe tabelul CategoriesView Data și adăugați câteva categorii:

Id Name
1 Tehnologie
2 Sport
3 Cultură

Apoi faceți același lucru pentru tabelul Articles:

Id Title Content PublishedAt CategoryId
1 Inteligența artificială în educație Universitățile experimentează noi metode de predare. 2026-03-10 1
2 Startul sezonului de Formula 1 Echipele prezintă noile monoposturi pentru sezon. 2026-03-15 2
3 Festival de film european Proiecții speciale și regizori invitați. 2026-03-09 3
4 Noua generație de procesoare Performanțe mai bune și consum redus de energie. 2026-03-12 1
5 Turneu internațional de tenis Jucători din topul mondial participă la competiție. 2026-03-11 2

Notă: CategoryId trebuie să corespundă unui Id existent din tabelul Categories.

Dependency Injection

Deoarece am înregistrat AppDbContext în containerul de DI (în Program.cs), îl putem primi automat prin constructor în orice clasă gestionată de framework.

În clasa IndexModel (code-behind-ul paginii Razor) adăugăm:

private readonly AppDbContext _context;

public IndexModel(AppDbContext context)
{
    _context = context;
}

Framework-ul creează automat instanța contextului și o pasează constructorului.

Razor Pages – Pagina Index

Pagina Index va afișa lista știrilor.

Index.cshtml.cs

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;

public class IndexModel : PageModel
{
    private readonly AppDbContext _context;

    public IndexModel(AppDbContext context)
    {
        _context = context;
    }

    public List<Article> Articles { get; set; }

    public void OnGet()
    {
        Articles = _context.Articles.ToList();
    }
}

Trimiterea datelor suplimentare cu ViewData / ViewBag

Pe lângă proprietățile publice, putem trimite date către pagină folosind ViewData — un dicționar string → object:

public void OnGet()
{
    ViewData["Title"] = "Lista de stiri";
    ViewData["Count"] = _context.Articles.Count();

    Articles = _context.Articles.ToList();
}

În .cshtml accesăm valorile astfel:

<h1>@ViewData["Title"]</h1>
<p>Total: @ViewData["Count"] articole</p>

ViewBag este un wrapper dinamic peste ViewData. Permite acces cu sintaxă de proprietate în loc de dicționar:

// in .cshtml.cs
ViewData["Title"] = "Stiri";       // ViewData - dictionar

// in .cshtml - ambele variante functioneaza:
@ViewData["Title"]
@ViewBag.Title

ViewBag și ViewData partajează aceleași date — sunt două moduri diferite de a accesa același dicționar.

Primirea parametrilor din URL (query string)

Metoda OnGet poate primi parametri direct din URL. De exemplu, pentru /Articles?category=sport:

public void OnGet(string? category)
{
    // category va avea valoarea "sport" din query string
}

Framework-ul face automat legătura între numele parametrului din metodă și cel din URL (model binding).

Index.cshtml

@page
@model IndexModel

<h1>Stiri</h1>

<table>
    <thead>
        <tr>
            <th>Title</th>
            <th>Date</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var article in Model.Articles)
        {
            <tr>
                <td>@article.Title</td>
                <td>@article.PublishedAt.ToShortDateString()</td>
            </tr>
        }
    </tbody>
</table>

Rutele în Razor Pages

În laboratorul anterior am definit rute explicit pe controllere cu [Route()] și [HttpGet()]. În Razor Pages, rutele sunt generate automat pe baza structurii de foldere din Pages/:

Fișier Ruta generată
Pages/Index.cshtml / sau /Index
Pages/Articles/Index.cshtml /Articles sau /Articles/Index
Pages/Articles/Details.cshtml /Articles/Details

Nu trebuie să configurăm nimic — framework-ul mapează automat calea fișierului la URL.

Pentru a adăuga parametri în rută, folosim directiva @page cu un template:

@page "{id:int}"

Aceasta face ca pagina să răspundă la /Articles/Details/5, unde 5 este valoarea parametrului id. Parametrul va fi disponibil în metoda OnGet(int id).

Fluxul request-ului

Browser → Razor Page → DbContext → SQL Server → DbContext → Razor Page → HTML

Referință — Metode EF Core utilizate

Metodă Rol
DbContext Conexiunea la baza de date
DbSet<T> Reprezintă un tabel
Include() Încarcă relații (eager loading)
ToList() Execută query-ul și returnează o listă
SaveChanges() Salvează modificările

Referință — Metode Razor Pages utilizate

Metodă Rol
PageModel Code-behind pentru pagină
OnGet() Handler pentru request GET
Page() Returnează pagina curentă
RedirectToPage() Redirect către altă pagină
ModelState.IsValid Verifică validarea modelului

Documentație EF Core: https://learn.microsoft.com/en-us/ef/core/

Documentație Razor Pages: https://learn.microsoft.com/en-us/aspnet/core/razor-pages/

Exerciții

Pornind de la pagina Index prezentată mai sus, extindeți aplicația pas cu pas.