🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

Entity Framework Core — DbContext, migracje i CRUD od podstaw

Entity Framework Core (EF Core) to ORM (Object-Relational Mapper) — zamiast pisać ręczny SQL, operujesz na obiektach C#, a EF Core sam generuje zapytania. To inny wybór niż Dapper (mikro-ORM, gdzie SQL piszesz ręcznie) — EF Core robi więcej za Ciebie kosztem mniejszej kontroli nad dokładnym zapytaniem.

Instalacja pakietów

dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design

Encja — zwykła klasa C#

public class Produkt
{
    public int Id { get; set; }
    public string Nazwa { get; set; } = string.Empty;
    public decimal Cena { get; set; }
}

Żadnych atrybutów, żadnego dziedziczenia z klasy bazowej — EF Core konwencjonalnie rozpoznaje Id jako klucz główny.

DbContext — most między klasami a bazą danych

public class SklepDbContext : DbContext
{
    public DbSet<Produkt> Produkty { get; set; } = null!;

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite("Data Source=sklep.db");
}

DbSet<Produkt> reprezentuje tabelę Produkty w bazie danych — każdy wiersz to jeden obiekt Produkt. DbContext śledzi zmiany na tych obiektach i tłumaczy je na SQL, gdy wywołasz SaveChanges().

Migracje — baza danych generowana z modelu C#

dotnet ef migrations add InitialCreate
dotnet ef database update

Pierwsza komenda analizuje klasy DbSet w Twoim DbContext i generuje plik migracji z metodami Up() (co zrobić, żeby dojść do tego stanu bazy) i Down() (jak to cofnąć):

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Produkty",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("Sqlite:Autoincrement", true),
                Nazwa = table.Column<string>(nullable: false),
                Cena = table.Column<decimal>(nullable: false)
            },
            constraints: table => table.PrimaryKey("PK_Produkty", x => x.Id));
    }

    protected override void Down(MigrationBuilder migrationBuilder)
        => migrationBuilder.DropTable(name: "Produkty");
}

Druga komenda (dotnet ef database update) faktycznie wykonuje ten SQL na bazie danych. Każda kolejna zmiana modelu (nowa właściwość, nowa klasa) to kolejna migracja — historia zmian struktury bazy danych żyje w kodzie, obok reszty projektu.

CRUD — Create

using var db = new SklepDbContext();
db.Produkty.Add(new Produkt { Nazwa = "Klawiatura", Cena = 149.99m });
db.SaveChanges();

Add tylko oznacza obiekt jako “do wstawienia” — żaden SQL nie wykonuje się, dopóki nie wywołasz SaveChanges().

CRUD — Read

var wszystkie = db.Produkty.ToList();
var jeden = db.Produkty.Find(1);
var drogie = db.Produkty.Where(p => p.Cena > 100).ToList();

Find szuka po kluczu głównym (najpierw sprawdza pamięć podręczną DbContext, potem bazę danych). Where z LINQ tłumaczy się na WHERE w SQL.

CRUD — Update

var produkt = db.Produkty.Find(1);
produkt.Cena = 129.99m;
db.SaveChanges();

Żadnego jawnego Update(). EF Core śledzi zmiany na obiektach pobranych z bazy (change tracking) — wystarczy zmienić właściwość, a SaveChanges() sam wygeneruje odpowiedni UPDATE. To kluczowa różnica względem Dappera, gdzie musisz ręcznie napisać zapytanie UPDATE z każdą kolumną do zmiany.

CRUD — Delete

var produkt = db.Produkty.Find(1);
db.Produkty.Remove(produkt);
db.SaveChanges();

Kiedy EF Core, kiedy Dapper

EF Core sprawdza się, gdy chcesz szybko modelować dane jako obiekty i nie potrzebujesz mikrozarządzania każdym zapytaniem — zmiana modelu to migracja, nie ręczne pisanie ALTER TABLE. Dapper sprawdza się, gdy zależy Ci na maksymalnej kontroli nad dokładnym SQL-em (złożone raporty, zoptymalizowane zapytania) i akceptujesz, że migracje i change tracking piszesz sam. Wielu projektów .NET używa obu naraz: EF Core do standardowego CRUD-u, Dapper do wydajnościowo krytycznych zapytań raportowych.

Co dalej

Po opanowaniu podstaw zobacz, jak testować kod korzystający z EF Core w Testowanie EF Core — InMemory vs SQLite, i jak EF Core wpisuje się w szerszą architekturę aplikacji w Clean Architecture (gdzie DbContext żyje w warstwie Infrastructure, za interfejsem repozytorium).

Podsumowanie

Entity Framework Core to encje (zwykłe klasy C#), DbContext z właściwościami DbSet<T> reprezentującymi tabele, migracje generujące i aktualizujące strukturę bazy danych z kodu (dotnet ef migrations add / database update), oraz CRUD operujący na obiektach zamiast na SQL-u — z automatycznym śledzeniem zmian, które sprawia, że Update to po prostu zmiana właściwości obiektu. To narzędzie o innym kompromisie niż Dapper: mniej kontroli nad dokładnym zapytaniem, w zamian za dużo mniej ręcznie pisanego kodu na co dzień.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →