Lekce 1 - Úvod do Entity Framework Core v C# .NET
Vítejte u první lekce kurzu o programování databázových aplikací s použitím technologie Entity Framework Core. Po absolvování tohoto kurzu budeme umět pracovat s relačními databázemi zcela objektovým přístupem, pouze v jazyce C# .NET. Při komunikaci s jakoukoliv databází tak již nebudeme muset napsat ani jeden řádek SQL kódu.
V dnešním Entity Framework Core tutoriálu si uvedeme různé způsoby práce s relačními databázemi v C# .NET a porovnáme je s fungováním Entity Framework Core.
Požadavky na znalosti
Tento kurz předpokládá kromě znalostí v rozsahu:
- Základní konstrukce jazyka C# .NET,
- Objektově orientované programování v C# .NET a
- Kolekce Seznam, Slovníky a LINQ v C# .NET,
taktéž:
- minimálně teoretické znalosti z kurzu Databáze v C# - ADO.NET a
- základní znalosti relačních databázových technologií spolu s jazykem SQL.
Přestože v tomto kurzu nebudeme psát žádný SQL kód, tak je dobré mu alespoň rozumět. A to především při snaze o různé optimalizace nebo při hledání chyb ve vygenerovaném SQL kódu. Námi napsaný C# kód se totiž musí nakonec vždy přeložit na SQL příkazy, které jsou volány na samotné databázi.
V kurzu budeme pracovat především s MS-SQL databází, s níž se můžete blíže seznámit v samostatném kurzu jen o MS-SQL databázi MS-SQL databáze krok za krokem.
Přístupy k relační databázi v .NET
Relační databáze jsou časem ověřený způsob jak pracovat s daty. Díky tomu tento typ databází dominuje celému trhu a jejich použití se tak při tvorbě aplikací mnohdy nevyhneme. Relační databáze však na rozdíl od jazyka C# nefungují objektově a komunikace s nimi tedy není úplně přímočará.
Z kurzu Databáze v C# - ADO.NET již víme, že v .NET existuje několik možností, jak se s tímto vypořádat. V rychlosti si je připomeneme.
Existují i databáze plně objektové. Příkladem takové databáze je třeba MongoDB. Více v lekci Úvod do MongoDB.
Neobjektové programování
První možností je samozřejmě programovat úplně bez objektů. Tím bychom však šli proti proudu, nemohli bychom používat žádné komponenty třetích stran a náš kód by byl velmi nekvalitní. Jelikož C# je objektový jazyk, ani by to v něm dost dobře nešlo.
V .NET se tomuto přístupu nejvíce blíží použití třídy
SqlCommand
, pomocí které lze přímo na databázi zasílat SQL
dotazy. Data z databáze jsou nám vrácena v podobě pole hodnot. Ukažme si
použití této třídy na jednoduchém příkladu databáze s tabulkou
uživatelů Users
:
FirstName | LastName | BirthDate |
---|---|---|
Jan | Novák | 11.3.1984 |
Michaela | Slavíková | 14.8.1990 |
Josef | Nový | 20.12.1972 |
Kód pro vypsání všech uživatelů seřazených podle data narození vypadá následovně:
using (SqlConnection connection = new SqlConnection(ConnectionString)) { connection.Open(); string query = "SELECT FirstName, LastName FROM Users ORDER BY BirthDate;"; SqlCommand command = new SqlCommand(query, connection); using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { Console.WriteLine($"Jméno: {reader.GetString(0)}, Příjmení: {reader.GetString(1)}"); } } }
Databázový Wrapper
Přístup tzv. wrapperu nám umožňuje s databází pracovat jako s objektem, nicméně komunikujeme s ní stále v jejím jazyce SQL. Mícháme tedy objektový a relační kód. Přístup je jakýmsi kompromisem a vyžaduje filozofii OOP trochu ohnout. Výhodou je zachování výkonu a schopností databáze za cenu mírné degradace myšlenek OOP.
Data z databáze vidíme nejčastěji jako hodnoty v tabulce (ta je objektem) a přicházíme o možnost přidělit entitám nějakou funkcionalitu. Tu místo toho sdružujeme do tzv. manažerů. Lze i částečně mapovat data na existující třídy, nicméně plnohodnotného konceptu objektového modelu nedosáhneme.
V .NET je tento přístup realizován třídami SqlDataAdapter
a
DataSet
. DataSet
v sobě obsahuje tabulky, tabulka
řádky a řádek sloupce. Tabulka je objekt, můžeme do ní řádky přidávat
a upravovat je bez psaní SQL kódu. Když chceme spustit na databázi nějaký
příkaz, použijeme DataAdapter
, pomocí kterého si naplníme
DataSet
daty. Příkazy již musíme psát v jazyce SQL dané
databáze.
Stejný výpis tabulky z příkladu výše bychom s použitím tříd
SqlDataAdapter
a DataSet
provedli takto:
using (SqlConnection connection = new SqlConnection(ConnectionString)) { connection.Open(); string query = "SELECT FirstName, LastName FROM Users ORDER BY BirthDate;"; using (SqlDataAdapter adapter = new SqlDataAdapter(query, connection)) using (DataSet result = new DataSet()) { adapter.Fill(result); foreach (DataRow row in result.Tables[0].Rows) { Console.WriteLine($"Jméno: {row["FirstName"]}, Příjmení: {row["LastName"]}"); } } }
Máme tedy určitou objektovou abstrakci, s tabulkami pracujeme objektově, ale data jsou stále jen sloupečky v tabulce. Také stále používáme jazyk SQL.
Objektově relační mapování
Objektově relační mapování (ORM) jde ortodoxně za myšlenkou OOP. Z databáze tedy místo pole hodnot dostáváme rovnou objekty a ty na sobě mají metody. V jazyce SQL vůbec nekomunikujeme. Tabulky v databázi vidíme jako kolekce objektů, se kterými můžeme pracovat běžnými prostředky jazyka. Jsme vlastně úplně odstíněni od toho, že pracujeme s relační databází. Zní to skvěle, že?
Háček je samozřejmě v tom, že na pozadí dochází k velké degradaci výkonu databáze, SQL dotazy se generují automaticky a jsou často neefektivní. Dalším problémem ORM je, že je velmi složité (ne k použití, ale k naprogramování).
.NET naštěstí perfektně odladěné ORM nabízí, je jím právě Entity Framework Core.
Názory na ORM jsou velmi kontroverzní. Například že samotná jeho myšlenka je nesprávná, jelikož generovaný SQL kód zkrátka nemůže být efektivní a je nutné pomýšlet na jeho konečnou podobu. Odstínění od práce s relační databází tedy není úplné.
Entity Framework Core
Entity Framework Core (EF Core) je tedy tzv. Objektově Relační Mapper (ORM) pro přístup k databázím. Vychází ze staršího Entity Frameworku, na rozdíl od kterého je ale rozšiřitelný, zcela open source a především multiplatformní.
Tuto technologii vyvíjí přímo Microsoft, má tak vcelku jistou dlouhodobou podporu.
Jak EF Core zhruba funguje
Abychom měli porovnání všech možností práce s databází kompletní, tak si nakonec ještě nastíníme, jak v Entity Framework Core funguje samotný přístup k databázi. Podrobně si vše však rozebereme až v budoucích lekcích. Následující kód tedy berme čistě jen jako ukázku, nemusíme v něm zatím úplně všemu rozumět.
Přístup k databázi je v Entity Framework Core realizován přes tzv. model, který představuje popis struktury databáze ve formě C# tříd. Tento popis se skládá zejména z entit a databázového kontextu.
Entita
Pod entitou si můžeme jednoduše představit tabulku databáze, kterou v C# reprezentujeme třídou. Například entita přestavující uživatele z příkladu výše by mohla vypadat následovně:
public class User { public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } }
Jednotlivé záznamy tabulky Users
pak chápeme jako instance
této třídy User
.
Záznamy tabulky budeme někdy zjednodušeně označovat taktéž pojmem "entita".
Databázový kontext
Databázový kontext pak představuje a definuje samotné propojení
s databází, všechny tabulky databáze (entity) a
vazby mezi nimi. Databázový kontext s tabulkou
Users
z příkladu výše by mohl vypadat třeba takto:
public class AppDbContext : DbContext { public DbSet<User> Users { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(ConnectionString); } }
Čtení dat
Jakékoliv dotazy na databázi následně voláme prostřednictvím instance našeho databázového kontextu:
using (AppDbContext dbContext = new AppDbContext()) { IList<User> users = dbContext.Users.OrderBy(user => user.LastName).ToList(); foreach (User user in users) { Console.WriteLine($"Jméno: {user.FirstName}, Příjmení: {user.LastName}"); } }
Vidíme, že pro získání všech uživatelů z databáze jsme nemuseli do C# kódu vmíchat žádný SQL příkaz a udrželi tak jeho jednoduchost a především čitelnost. S tabulkami databáze pracujeme podobným způsobem jako s kolekcemi. Dobrá čitelnost je zajištěna i díky LINQ metodám, které Entity Framework Core podporuje pro vybírání dat z databáze.
LINQ metody jsme probírali v lekci LINQ v C# .NET - Revoluce v dotazování.
Poskytovatelé databází
Výše zmiňovaná rozšiřitelnost souvisí s tím, že přes Entity Framework Core můžeme přistupovat k mnoha různým databázím prostřednictvím dodatečných knihoven označovaných jako poskytovatelé databází (database providers). Přímo Microsoft například vyvíjí poskytovatele pro MS-SQL a SQLite databáze. Poskytovatelé pro PostgreSQL, Oracle nebo MySQL databáze jsou však již vyvíjeni třetími stranami.
Poskytovatele databází si, stejně jako samotný Entity Framework Core, instalujeme skrze NuGet balíčky. Pro výše uvedené databáze to jsou například balíčky:
- Microsoft.EntityFrameworkCore.SqlServer pro MS-SQL databáze,
- Microsoft.EntityFrameworkCore.Sqlite pro SQLite databáze,
- Npgsql.EntityFrameworkCore.PostgreSQL pro PostgreSQL databáze,
- MySql.EntityFrameworkCore nebo Pomelo.EntityFrameworkCore.MySql pro MySQL databáze a
- Oracle.EntityFrameworkCore pro Oracle databáze.
Seznam těch nejvýznamnějších poskytovatelů nalezneme v oficiální dokumentaci.
To by bylo pro úvodní lekci vše.
V příští lekci, Instalace a první model v Entity Framework Core a C# .NET, si založíme náš první projekt s Entity Framework Core a vytvoříme jednoduchý model.