Lekce 10 - Scaffolding a Entity Framework v ASP.NET Core MVC
V minulé lekci, Úprava layotu a obsahových stránek v ASP.NET Core MVC , jsme dokončili úpravu struktury výchozí MVC šablony a obsahových stránek tak, aby vypadaly jako osobní blog.
Dnešní ASP.NET Core tutoriál bude nabitý nejmodernějšími technologiemi, pomocí kterých vytvoříme během okamžiku administraci článků na našem blogu. Budeme tak moci na něm psát, ukládat a prezentovat vlastní články.
Budeme pokračovat s projektem z lekce Úprava layotu a obsahových stránek v ASP.NET Core MVC.
Scaffolding
Webové frameworky jsou koncipované tak, aby co nejvíce zjednodušily práci programátora a zejména zredukovaly množství kódu, které musí napsat. Ačkoli se jim to daří, tak stále existují situace, kde je určité množství stereotypního kódu nutné a neexistuje způsob, jakým by ho framework mohl nahradit.
Chystáme se programovat administraci článků na webu a proto budeme potřebovat:
- kontroler,
- model článku,
- databázi pro uložení článků,
- pohledy pro výpis, editace, přidání, odstranění a detail článků.
Tyto součásti aplikace jednoduše musí obsahovat a nezáleží na tom, v jak geniálním jazyce ji programujeme. Nemusí je však stereotypně psát programátor, ale může je za nás vygenerovat IDE. Tomuto principu se říká scaffolding (nejlepší překlad je asi lešení, kostra). Visual Studio nám jednoduše předgeneruje databázi, kontroler a pohledy. Získáme tak kostru se základní funkčností, kterou pouze upravíme.
Uložení dat
Námi napsané články budeme muset někam ukládat, budeme tedy potřebovat
nějaké úložiště dat. K tomu nám poslouží relační
databáze. Tento pojem označuje databázi založenou na tabulkách.
Každá tabulka obsahuje položky jednoho typu. Můžeme mít tedy tabulku
Uzivatele
, další tabulku Clanky
a další třeba
Komentare
.
Databázovou tabulku si můžeme představit třeba jako tabulku v Excelu. Tabulka Uzivatele
by
mohla vypadat asi takto:
Jméno | Příjmení | Datum narození | Počet článků |
---|---|---|---|
Jan | Novák | 11.3.1984 | 17 |
Tomáš | Marný | 1.2.1989 | 6 |
Josef | Nový | 20.12.1972 | 9 |
Michaela | Slavíková | 14.8.1990 | 1 |
Položky (konkrétně zde uživatele) ukládáme na jednotlivé řádky, sloupce pak označují atributy (vlastnosti, chcete-li), které položky mají. Každý sloupec má pevně stanovený datový typ (číslo, znak, krátký text, dlouhý text...) a může obsahovat hodnoty jen tohoto typu.
Pokud chceme s relační databází rozumně pracovat, každý řádek v tabulce by měl být opatřený unikátním identifikátorem. U uživatelů by to mohlo být třeba rodné číslo, mnohem častěji se však používají identifikátory umělé a to tak, že uživatele prostě očíslujeme. V relačních databázích se tento identifikátor též nazývá primární klíč.
MS-SQL databáze
V tomto kurzu využijeme relační databázi typu MS-SQL. Jedná se o velmi sofistikovaný a odladěný nástroj, který za nás řeší spoustu problémů a zároveň je extrémně jednoduchý k použití. S databází komunikujeme jazykem SQL, kterým jsou v podstatě lidsky srozumitelné věty. Nad tímto jazykem vzniklo mnoho nadstaveb a někdy se už dokonce s SQL vůbec nesetkáme. To bude právě i náš případ. S relační databází totiž budeme pracovat pomocí technologie Entity Framework Core.
V naší aplikaci využijeme pouze odlehčenou verzi MS-SQL databáze označovanou jako LocalDB. Tato databáze se nejčastěji používá buďto při vývoji aplikace, a ve finální verzi je nahrazena plnohodnotnou databází, anebo někdy i jako lokální úložiště dat.
Ve druhém případě se však spíše volí databáze SQLite.
Entity Framework Core
Entity Framework Core je tzv. Objektově Relační Mapper (ORM) pro přístup k databázím. Databázové tabulky se přímo mapují na C# třídy, v kódu pracujeme jen s objekty a framework sám na pozadí generuje SQL kód. S jazykem SQL vůbec nepřijdeme do styku a naše aplikace je 100% objektová.
Přístup k databázi je v Entity Framework Core realizován přes tzv. model. Jedná se o popis struktury databáze ve formě C# tříd, který se skládá zejména z entit a databázového kontextu.
Pod entitou si můžeme jednoduše představit tabulku databáze, kterou v C# reprezentujeme třídou. Databázový kontext pak představuje a definuje:
- samotné propojení s databází,
- všechny tabulky databáze (entity) a
- vazby mezi nimi.
Veškerá komunikace s databází probíhá skrze tento databázový kontext.
Code First a Database First přístupy
S Entity Frameworkem lze pracovat dvěma způsoby. Můžeme vytvořit C# třídu (entitu) a Entity Framework Core nám podle ní automaticky vygeneruje databázovou tabulku a potřebný databázový kontext. Tomuto přístupu se říká Code First.
Druhý způsob spočívá v založení databáze, ze které nám Entity Framework vygeneruje entity a databázový kontext. Tento přístup se jmenuje Database First.
Jelikož vytvořit třídu je mnohem jednodušší než vytvořit databázi, zvolíme pro tento tutoriál přístup Code First. I v praxi se jedná o častější volbu.
Podrobně se technologie Entity Framework Core rozebírá buď v samostatném kurzu Entity Framework Core v C# .NET zde na síti, anebo případně v kurzu tvorby webového API ASP.NET Core Web API, ve kterém si přesně ukazujeme, jak Entity Framework Core manuálně napojit na ASP.NET Core aplikaci.
Model databáze
Co se týče modelu databáze, tak zde nám bude stačit vytvořit pouze
entitu článku, databázový kontext totiž už máme v projektu vygenerovaný.
Jedná se o třídu ApplicationDbContext
ve složce
Data/
. Tato třída nám byla vygenerována již během
zakládání projektu, protože jsme zaškrtli možnost Individual
Accounts pro podporu autentizace v naší aplikaci. Účty uživatelů
naší aplikace se totiž budou ukládat taktéž do databáze. V tomto kontextu
jsou zatím zahrnuty pouze tabulky pro uživatelské účty a role.
Entita Article
Protože Entity Framework Core používá určité konvence a převádí
názvy tříd do množného čísla, budeme aplikaci psát anglicky, aby názvy
nebyly zkomolené. Do složky Models/
si tedy přidejme novou
třídu Article
. Bude se jednat o naši entitu článku, na
základě které se vygeneruje tabulka v databázi. Vlastnosti entity budou
představovat jednotlivé sloupce databázové tabulky.
Třída bude vypadat takto:
public class Article { public int Id { get; set; } public string Content { get; set; } = ""; public string Title { get; set; } = ""; public string Description { get; set; } = ""; }
Máme zde několik vlastností, konkrétně:
Id
– unikátní identifikátor článku,Content
– obsah článku,Title
– titulek článku,Description
– popisek článku.
Id
musí mít každá entita. Až z kódu vygenerujeme tabulky,
tak se stane jejím primárním klíčem, který bude články jednoznačně
odlišovat i kdyby měly třeba stejný titulek.
Od Visual Studio 2022 jsou všechny projekty automaticky
generovány s povolenou funkcí nullovatelných referenčních
typů (funkce přidána v C# 8.0). Tato funkce nám umožňuje nastavit
hodnotu null
pouze atributům nebo vlastnostem, které mají
definici typu označenou operátorem ?
, a to i v případě že se
jedná o referenční datový typ. Každý atribut nebo
vlastnost referenčního datového typu tak musí mít při vytváření objektu
nastavenou nějakou výchozí hodnotu jinou než null
, pokud není
označen jako nullovatelný operátorem ?
. Proto našim vlastnostem
nastavujeme jako výchozí hodnotu prázdný řetězec.
Rebuild
Aby uměl Entity Framework Core databázi ze třídy Article
vygenerovat, musíme projekt s touto třídou nejprve sestavit (provést
rebuild). Toho dosáhneme kliknutím pravým na projekt v Solution
Explorer a výběrem možnosti Rebuild. Třída se tím
zkompiluje:
Kontroler
ArticlesController
Začneme tvorbou zmiňovaného kontroleru pro správu článků. Přidáme si
jej do složky Controllers/
kliknutím pravým tlačítkem na danou
složku a volbou Add → Controller.... Zobrazí se nám
dialog s výběrem scaffoldingu, kde zvolíme MVC Controller with views,
using Entity Framework:
Jako model zvolíme naši třídu Article
a jako datový kontext
vybereme již existující kontext ApplicationDbContext
. Název
kontroleru ponecháme výchozí ArticlesController
a dialog
potvrdíme:
Všimněme si, že můžeme nastavit, zda chceme používat layout a několik dalších věcí.
Co se vygenerovalo
Pouze tímto jedním krokem se nám do projektu doinstalovaly všechny potřebné NuGet balíčky s Entity Framework Core a vygenerovaly nebo rozšířily se zbývající třídy modelu databáze.
Dále nám byl vygenerován samotný kontroler
ArticlesController
s několika akcemi. Nalezneme zde akce:
Index()
– výpis všech článků,Details()
– výpis jednoho článku,Create()
– vytvoření článku,Edit()
– editace článku,Delete()
– odstranění článku.
Když se podíváme do složky Views/
, nalezneme zde složku
Articles/
a v ní pět pohledů pro tyto akce.
Migrace
Sice již máme vygenerovaný celý databázový model, ale zatím v
databázi neexistuje tabulka pro naši entitu
Article
a dokonce ani samotná databáze. Aby databáze odpovídala
našim modelům, musíme na ní provádět tzv. migrace. Jedná
se o třídy popisující, jak se má databáze nebo její tabulky
vygenerovat.
Ve Visual Studio vybereme v horním menu Tools → NuGet Package
Manager → Package Manager Console. V dolní části okna se nám
otevře konzole, do které zadáme příkaz Add-Migration
pro
vygenerování migrace:
Add-Migration ArticleMigration
ArticleMigration
je název naší migrace, můžeme zadat i
jakýkoli jiný. Potvrdíme klávesou Enter. Po chvilce se nám ve
složce Data/Migrations/
vygeneruje nová třída
ArticleMigration
představující danou migraci.
Jakmile migraci vytvoříme, musíme ji na databázi aplikovat. To provedeme
příkazem Update-Database
:
Kdykoli rozšíříme databázový model o novou třídu nebo nějakou již existující třídu odstraníme nebo změníme (např. do něj přidáme nějakou vlastnost, nějakou odstraníme apod.), provedeme tento postup. Visual Studio za nás aktualizuje databázi, aby v ní byly všechny změny promítnuté.
Testování
Spustíme aplikaci a přejdeme na odkaz Články:
Vidíme, že administrace článků se vygenerovala automaticky a je plně funkční. Nejedná se sice zatím o nějaký skvost, v budoucích lekcích to však hravě napravíme. Můžeme si zkusit přidat nějaký testovací článek odkazem Create New:
Zobrazí se nám v seznamu článků:
A můžeme si ho také otevřít pomocí odkazu Details:
Získání článku z databáze
Všimněme si URL adresy:
http://localhost:44311/Articles/Details/1
První segment cesty označuje název kontroleru
ArticlesController
, který se má spustit. Druhým segmentem je
název jeho akce Details()
.
O tomto mechanismu, který převádí cestu URL adresy na
volání metod kontroleru (routování) jsme si již říkali v předchozích
lekcích. Jeho nastavení můžeme měnit v souboru Program.cs
.
V předchozích aplikacích jsme měli v kontroleru
HomeController
vždy jen jednu akci Index()
a ta se
spustí automaticky, i když žádnou cestu neuvedeme. V souboru
Program.cs
totiž máme nastavené výchozí směrování na akci
Index()
kontroleru HomeController
:
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
Další segmenty v cestě jsou parametry dané akce v
kontroleru, zde se jedná o Id
článku, který zobrazujeme. Ze
vzoru cesty můžeme vyčíst, že takový parametr je nepovinný (znak
otazníku ?
) a že se akci předává skrze parametr metody s
názvem id
.
Kontroler
ArticlesController
Akce Details()
v kontroleru ArticlesController
vypadá takto:
// GET: Articles/Details/5 public async Task<IActionResult> Details(int? id) { if (id == null) { return NotFound(); } var article = await _context.Article .FirstOrDefaultAsync(m => m.Id == id); if (article == null) { return NotFound(); } return View(article); }
Opravdu vidíme, že nullovatelný parametr id
je zapsán jako
obyčejný parametr metody. V případě, že parametr není zadán nebo
článek není nalezen, je vrácena odpověď s chybou.
Získání článku z databáze má na svědomí jen tento řádek:
var article = await _context.Article.FirstOrDefaultAsync(m => m.Id == id);
Třídní atribut _context
obsahuje instanci databázového
kontextu ApplicationDbContext
, ve kterém je tabulka s články
reprezentována vlastností Article
. Z této tabulky se jeden
záznam získá jednoduše zavoláním metody
FirstOrDefaultAsync()
. Pokud jste Entity Framework Core neznali,
tak ho nyní asi budete milovat
Asynchronní akce
Na této akci zároveň vidíme, že akce kontroleru můžeme klidně zapisovat i jako tzv. asynchronní metody, pokud to potřebujeme. ASP.NET Core si s tím poradí.
Jedná se o metody, které nevrací svou hodnotu ihned, ale až po nějakém
časovém intervalu. Mezitím program na návratovou hodnotu volané metody
nečeká, ale pokračuje dále ve vykonávání kódu. Takovou metodou je
právě metoda FirstOrDefaultAsync()
, která vrací svou hodnotu
až poté, co ji získá z databáze.
Kód akce následující po volání metody
FirstOrDefaultAsync()
smí být vykonán až ve chvíli, kdy je k
dispozici návratová hodnota této metody. Na návratovou hodnotu je tedy
potřeba čekat. Proto se metoda FirstOrDefaultAsync()
volá s
klíčovým slovem await
. Tím se zajistí čekání na návratovou
hodnotu a vykonávání akce se pozdrží. Výhodou asynchronních metod je, že
procesor může v průběhu čekání vykonávat jakýkoliv jiný kód a
efektivněji tak pracovat se svým časem.
Aby se v akci Details()
mohla volat asynchronní metoda s
klíčovým slovem await
, tak musí být sama asynchronní metodou.
Toho se dosáhne označením metody klíčovým slovem async
.
Více o tomto tématu se dočtete v kurzu Paralelní programování a vícevláknové aplikace v C# .NET.
Databáze je uložená v lokálním souboru, ovšem nikoli ve
složce projektu, ale ve vaší uživatelské složce v
C:\Users\<vaše jméno>
. Můžete se do ní podívat,
soubor s databází má příponu .mdf
. Toto také znamená, že
pokud vám něco nepůjde a budete si chtít dnes nebo kdykoli
příště stáhnout hotové řešení pod článkem, databáze v něm nebude a
projekt nebude fungovat. Po stažení projektu je nutné vygenerovat
databázi příkazem Update-Database
v Package Manager
Console, čímž se prázdná databáze pro příslušný projekt
vytvoří. Poté bude stažený projekt teprve fungovat.
V následujícím cvičení, Řešené úlohy k 8.-10. lekci frameworku ASP.NET Core MVC, si procvičíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 414x (4.93 MB)
Aplikace je včetně zdrojových kódů v jazyce C#