Kurz: Informace o pokročilých scénářích – ASP.NET MVC s EF Core
V předchozím kurzu jste implementovali dědičnost tabulek podle hierarchie. V tomto kurzu se seznámíte s několika tématy, která jsou užitečná, když překročíte základy vývoje webových aplikací ASP.NET Core, které používají Entity Framework Core.
V tomto kurzu:
- Provádění nezpracovaných dotazů SQL
- Zavolejte dotaz na vrácení entit
- Zavolejte dotaz pro návrat jiných typů
- Volání aktualizačního dotazu
- Prozkoumání dotazů SQL
- Vytvoření abstraktní vrstvy
- Informace o automatické detekci změn
- Další informace o EF Core zdrojovém kódu a plánech vývoje
- Naučte se používat dynamické LINQ ke zjednodušení kódu.
Požadavky
Provádění nezpracovaných dotazů SQL
Jednou z výhod použití Entity Frameworku je to, že se vyhne příliš úzkému vázání kódu na konkrétní metodu ukládání dat. Dělá to tak, že pro vás generuje dotazy a příkazy SQL, což vás zároveň osvobozuje od nutnosti je psát sami. Existují ale výjimečné scénáře, kdy potřebujete spustit konkrétní dotazy SQL, které jste vytvořili ručně. Pro tyto scénáře obsahuje rozhraní API Entity Framework Code First metody, které umožňují předávat příkazy SQL přímo do databáze. V EF Core 1.0 máte následující možnosti:
Pro dotazy, které vracejí typy entit, použijte metodu
DbSet.FromSql
. Vrácené objekty musí být typu očekávaného objektemDbSet
a jsou automaticky sledovány kontextem databáze, pokud vypnout sledování.Pro příkazy bez dotazu použijte
Database.ExecuteSqlCommand
.
Pokud potřebujete spustit dotaz, který vrací typy, které nejsou entitami, můžete použít ADO.NET s připojením k databázi, které poskytuje EF. Vrácená data nejsou sledována kontextem databáze, i když tuto metodu používáte k načtení typů entit.
Stejně jako vždy platí, když spouštíte příkazy SQL ve webové aplikaci, musíte podniknout preventivní opatření k ochraně webu před útoky prostřednictvím injektáže SQL. Jedním ze způsobů, jak to udělat, je použít parametrizované dotazy, aby se zajistilo, že řetězce odeslané webovou stránkou nelze interpretovat jako příkazy SQL. V tomto kurzu použijete parametrizované dotazy při integraci uživatelského vstupu do dotazu.
Spustit dotaz pro navrácení entit
Třída DbSet<TEntity>
poskytuje metodu, kterou můžete použít k provedení dotazu, který vrací entitu typu TEntity
. Pokud chcete zjistit, jak to funguje, změníte kód v metodě Details
kontroleru oddělení.
V DepartmentsController.cs
nahraďte kód, který načítá oddělení, ve metodě Details
voláním metody FromSql
, jak je znázorněno v následujícím zvýrazněném kódu.
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
var department = await _context.Departments
.FromSql(query, id)
.Include(d => d.Administrator)
.AsNoTracking()
.FirstOrDefaultAsync();
if (department == null)
{
return NotFound();
}
return View(department);
}
Pokud chcete ověřit, že nový kód funguje správně, vyberte kartu Oddělení a pak Podrobnosti pro jedno z oddělení.
Zavolejte dotaz k vrácení jiných typů
Dříve jste vytvořili mřížku statistiky studenta pro stránku O aplikaci, která zobrazovala počet studentů pro každé datum registrace. Získali jste data ze sady entit Students (_context.Students
) a pomocí LINQ promítli výsledky do seznamu objektů zobrazení modelu EnrollmentDateGroup
. Předpokládejme, že chcete místo použití LINQ napsat samotný SQL. K tomu je potřeba spustit dotaz SQL, který vrací něco jiného než objekty entity. Ve verzi EF Core 1.0 je jedním ze způsobů, jak to udělat, napsat kód ADO.NET a získat připojení k databázi přes EF.
V HomeController.cs
nahraďte metodu About
následujícím kódem:
public async Task<ActionResult> About()
{
List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
var conn = _context.Database.GetDbConnection();
try
{
await conn.OpenAsync();
using (var command = conn.CreateCommand())
{
string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
+ "FROM Person "
+ "WHERE Discriminator = 'Student' "
+ "GROUP BY EnrollmentDate";
command.CommandText = query;
DbDataReader reader = await command.ExecuteReaderAsync();
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(0), StudentCount = reader.GetInt32(1) };
groups.Add(row);
}
}
reader.Dispose();
}
}
finally
{
conn.Close();
}
return View(groups);
}
Přidejte příkaz using:
using System.Data.Common;
Spusťte aplikaci a přejděte na stránku O aplikaci. Zobrazí stejná data, která zobrazovala dříve.
Spustit aktualizační dotaz
Předpokládejme, že správci Contoso University chtějí provádět globální změny v databázi, například změnit počet kreditů pro každý kurz. Pokud má univerzita velký počet kurzů, bylo by neefektivní je načíst všechny jako entity a změnit je jednotlivě. V této části implementujete webovou stránku, která uživateli umožní určit faktor, kterým se má změnit počet kreditů pro všechny kurzy, a provedete změnu spuštěním příkazu SQL UPDATE. Webová stránka bude vypadat jako na následujícím obrázku:
aktualizace stránky s kredity kurzu
V CoursesController.cs
přidejte metody UpdateCourseCredits pro HttpGet a HttpPost:
public IActionResult UpdateCourseCredits()
{
return View();
}
[HttpPost]
public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
{
if (multiplier != null)
{
ViewData["RowsAffected"] =
await _context.Database.ExecuteSqlCommandAsync(
"UPDATE Course SET Credits = Credits * {0}",
parameters: multiplier);
}
return View();
}
Když kontroler zpracuje požadavek HttpGet, nic se nevrátí v ViewData["RowsAffected"]
a zobrazení zobrazí prázdné textové pole a tlačítko odeslat, jak je znázorněno na předchozím obrázku.
Po kliknutí na tlačítko Update je volána metoda HttpPost a násobitel má hodnotu zadaná do textového pole. Kód pak spustí SQL, který aktualizuje kurzy a vrátí počet ovlivněných řádků do zobrazení v ViewData
. Když zobrazení získá RowsAffected
hodnotu, zobrazí se počet aktualizovaných řádků.
Ve Průzkumníku řešeníklikněte pravým tlačítkem myši na složku Views/Kurzy a potom klikněte na Přidat > Nová položka.
V dialogovém okně Přidat novou položku klikněte na ASP.NET Core v části Nainstalované v levém podokně, klikněte na Razor Zobrazita pojmenujte nové zobrazení UpdateCourseCredits.cshtml
.
V Views/Courses/UpdateCourseCredits.cshtml
nahraďte kód šablony následujícím kódem:
@{
ViewBag.Title = "UpdateCourseCredits";
}
<h2>Update Course Credits</h2>
@if (ViewData["RowsAffected"] == null)
{
<form asp-action="UpdateCourseCredits">
<div class="form-actions no-color">
<p>
Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
</p>
<p>
<input type="submit" value="Update" class="btn btn-default" />
</p>
</div>
</form>
}
@if (ViewData["RowsAffected"] != null)
{
<p>
Number of rows updated: @ViewData["RowsAffected"]
</p>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Spusťte metodu UpdateCourseCredits
tak, že vyberete kartu Courses a pak na konec adresy URL v adresním řádku prohlížeče přidáte "/UpdateCourseCredits" (například: http://localhost:5813/Courses/UpdateCourseCredits
). Do textového pole zadejte číslo:
Klikněte na Aktualizovat. Zobrazí se počet ovlivněných řádků:
Kliknutím na Zpět na seznam zobrazíte seznam kurzů s upraveným počtem kreditů.
Mějte na paměti, že produkční kód zajistí, aby aktualizace vždy vedlo k platným datům. Zjednodušený kód, který je zde uvedený, by mohl vynásobit počet kreditů, aby výsledkem byla čísla větší než 5. (Vlastnost Credits
má atribut [Range(0, 5)]
.) Aktualizační dotaz by fungoval, ale neplatná data by mohla způsobit neočekávané výsledky v jiných částech systému, které předpokládají, že počet kreditů je 5 nebo méně.
Další informace o nezpracovaných dotazech SQL naleznete v tématu Nezpracované dotazy SQL.
Prozkoumání dotazů SQL
Někdy je užitečné vidět skutečné dotazy SQL, které se odesílají do databáze. Integrovaná funkce protokolování pro ASP.NET Core je automaticky využívána EF Core k zápisu protokolů, které obsahují SQL pro dotazy a aktualizace. V této části uvidíte několik příkladů protokolování SQL.
Otevřete StudentsController.cs
a v metodě Details
nastavte zarážku na příkaz if (student == null)
.
Spusťte aplikaci v režimu ladění a přejděte na stránku Podrobnosti pro studenta.
Přejděte do okna Výstup, které zobrazuje výstup ladění, a uvidíte dotaz:
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (56ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [s].[ID], [s].[Discriminator], [s].[FirstName], [s].[LastName], [s].[EnrollmentDate]
FROM [Person] AS [s]
WHERE ([s].[Discriminator] = N'Student') AND ([s].[ID] = @__id_0)
ORDER BY [s].[ID]
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (122ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT [s.Enrollments].[EnrollmentID], [s.Enrollments].[CourseID], [s.Enrollments].[Grade], [s.Enrollments].[StudentID], [e.Course].[CourseID], [e.Course].[Credits], [e.Course].[DepartmentID], [e.Course].[Title]
FROM [Enrollment] AS [s.Enrollments]
INNER JOIN [Course] AS [e.Course] ON [s.Enrollments].[CourseID] = [e.Course].[CourseID]
INNER JOIN (
SELECT TOP(1) [s0].[ID]
FROM [Person] AS [s0]
WHERE ([s0].[Discriminator] = N'Student') AND ([s0].[ID] = @__id_0)
ORDER BY [s0].[ID]
) AS [t] ON [s.Enrollments].[StudentID] = [t].[ID]
ORDER BY [t].[ID]
Tady si všimnete něčeho, co vás může překvapit: SQL vybere z tabulky Person až 2 řádky (TOP(2)
). Metoda SingleOrDefaultAsync
se na serveru nepřeloží na 1 řádek. Tady je důvod:
- Pokud dotaz vrátí více řádků, vrátí metoda hodnotu null.
- Pokud chcete zjistit, jestli dotaz vrátí více řádků, musí EF zkontrolovat, jestli vrátí alespoň 2.
Pamatujte, že k získání výstupu protokolování v okně výstupu nemusíte používat režim ladění a zastavovat na zarážce. Je to jen pohodlný způsob, jak zastavit protokolování v okamžiku, kdy se chcete podívat na výstup. Pokud to neuděláte, protokolování pokračuje a budete se muset posunout zpět, abyste našli části, které vás zajímají.
Vytvoření abstraktní vrstvy
Mnoho vývojářů píše kód pro implementaci úložiště a jednotek pracovních vzorů jako obálky kolem kódu, který funguje s Entity Framework. Tyto vzory jsou určeny k vytvoření abstraktní vrstvy mezi vrstvou přístupu k datům a vrstvou obchodní logiky aplikace. Implementace těchto vzorů může pomoct izolovat vaši aplikaci před změnami v úložišti dat a může usnadnit automatizované testování jednotek nebo vývoj řízený testy (TDD). Psaní dalšího kódu pro implementaci těchto vzorů ale není vždy nejlepší volbou pro aplikace, které používají EF, z několika důvodů:
Samotná třída kontextu EF izoluje váš kód od kódu specifického pro úložiště dat.
Třída kontextu EF může fungovat jako jednotka práce pro aktualizace databází při používání EF.
EF obsahuje funkce pro implementaci TDD bez psaní kódu úložiště.
Informace o tom, jak implementovat vzory úložiště a jednotky práce, naleznete v tématu ve verzi Entity Framework 5 této série kurzů.
Entity Framework Core implementuje zprostředkovatele databáze v paměti, který lze použít k testování. Další informace najdete v tématu Testování pomocí nástroje InMemory.
Automatická detekce změn
Entity Framework určuje, jak se entita změnila (a proto je potřeba do databáze odeslat aktualizace) porovnáním aktuálních hodnot entity s původními hodnotami. Původní hodnoty se ukládají při dotazování nebo připojení entity. Některé z metod, které způsobují automatickou detekci změn, jsou následující:
DbContext.SaveChanges
DbContext.Entry
SledovačZměn.Záznamy
Pokud sledujete velký počet entit a často voláte jednu z těchto metod ve smyčce, může dojít k významným vylepšením výkonu tím, že dočasně vypnete automatickou detekci změn pomocí vlastnosti ChangeTracker.AutoDetectChangesEnabled
. Například:
_context.ChangeTracker.AutoDetectChangesEnabled = false;
EF Core zdrojový kód a plány vývoje
Zdroj Entity Framework Core je v https://github.com/dotnet/efcore. Úložiště EF Core obsahuje noční sestavení, sledování problémů, specifikace funkcí, poznámky z návrhových schůzek a plán dalšího vývoje. Můžete souborovat nebo vyhledávat chyby a přispívat.
I když je zdrojový kód otevřený, Entity Framework Core se plně podporuje jako produkt Microsoftu. Tým Microsoft Entity Framework udržuje kontrolu nad tím, které příspěvky jsou přijaty, a testuje všechny změny kódu, aby se zajistila kvalita každé verze.
Zpětné inženýrství z existující databáze
Pokud chcete datový model zpětně analyzovat, včetně tříd entit z existující databáze, použijte příkaz scaffold-dbcontext. Podívejte se na úvodní kurz.
Zjednodušení kódu pomocí dynamického LINQ
třetí tutoriál v této sérii ukazuje, jak psát kód LINQ pomocí pevně zakódovaných názvů sloupců v příkazu switch
. S dvěma sloupci na výběr to funguje dobře, ale pokud máte mnoho sloupců, může být kód zbytečně složitý. Chcete-li tento problém vyřešit, můžete použít metodu EF.Property
k zadání názvu vlastnosti jako řetězce. Pokud chcete tento přístup vyzkoušet, nahraďte metodu Index
v StudentsController
následujícím kódem.
public async Task<IActionResult> Index(
string sortOrder,
string currentFilter,
string searchString,
int? pageNumber)
{
ViewData["CurrentSort"] = sortOrder;
ViewData["NameSortParm"] =
String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
ViewData["DateSortParm"] =
sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}
ViewData["CurrentFilter"] = searchString;
var students = from s in _context.Students
select s;
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
if (string.IsNullOrEmpty(sortOrder))
{
sortOrder = "LastName";
}
bool descending = false;
if (sortOrder.EndsWith("_desc"))
{
sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
descending = true;
}
if (descending)
{
students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
}
else
{
students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
}
int pageSize = 3;
return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(),
pageNumber ?? 1, pageSize));
}
Uznání
Tom Dykstra a Rick Anderson (twitter @RickAndMSFT) napsal tento kurz. Rowan Miller, Diego Vega a další členové týmu Entity Framework asistovali s revizemi kódu a pomohli ladit problémy, které vznikly při psaní kódu pro kurzy. John Parente a Paul Goldman pracovali na aktualizaci kurzu pro ASP.NET Core 2.2.
Řešení běžných chyb
ContosoUniversity.dll používané jiným procesem
Zpráva o chybě:
Nelze otevřít ... bin\Debug\netcoreapp1.0\ContosoUniversity.dll' for writing -- 'The process cannot access the file '...\bin\Debug\netcoreapp1.0\ContosoUniversity.dll' protože se používá jiným procesem.
Řešení:
Zastavte web ve službě IIS Express. Přejděte do oznamovací oblasti systému Windows, najděte službu IIS Express a klikněte pravým tlačítkem myši na její ikonu, vyberte stránku Contoso University a potom klikněte na Zastavit stránku.
Migrace strukturovaná bez kódu v metodách Up a Down
Možná příčina:
Příkazy CLI EF se automaticky nezavřou ani neuloží soubory kódu. Pokud máte při spuštění příkazu migrations add
neuložené změny, ef vaše změny nenajde.
Řešení:
Spusťte příkaz migrations remove
, uložte změny kódu a spusťte příkaz migrations add
znovu.
Chyby při spouštění aktualizace databáze
Při provádění změn schématu v databázi s existujícími daty je možné získat další chyby. Pokud dojde k chybám migrace, které nemůžete vyřešit, můžete změnit název databáze v připojovacím řetězci nebo databázi odstranit. U nové databáze nejsou k dispozici žádná data k migraci a příkaz update-database je mnohem pravděpodobnější, že se dokončí bez chyb.
Nejjednodušším přístupem je přejmenování databáze v appsettings.json
. Při příštím spuštění database update
se vytvoří nová databáze.
Chcete-li odstranit databázi v nástroji SSOX, klepněte pravým tlačítkem myši na databázi, klepněte na Odstranita potom v dialogovém okně Odstranit databázi vyberte Zavřít existující připojení a klepněte na tlačítko OK.
Pokud chcete odstranit databázi pomocí rozhraní příkazového řádku, spusťte příkaz database drop
CLI:
dotnet ef database drop
Chyba při vyhledání instance SQL Serveru
Zpráva o chybě:
Při navazování připojení k SQL Serveru došlo k chybě související se sítí nebo konkrétní instancí. Server nebyl nalezen nebo nebyl přístupný. Ověřte, že je název instance správný a zda je SQL Server nakonfigurovaný tak, aby povoloval vzdálená připojení. (zprostředkovatel: Síťová rozhraní SQL, chyba: 26 – Chyba při zjišťování zadaného serveru nebo instance)
Řešení:
Zkontrolujte připojovací řetězec. Pokud jste soubor databáze odstranili ručně, změňte název databáze v konstrukčním řetězci, abyste mohli začít s novou databází.
Získání kódu
Stáhnout nebo zobrazit dokončenou aplikaci.
Další zdroje informací
Další informace o EF Corenaleznete v dokumentaci Entity Framework Core. K dispozici je také kniha: Entity Framework Core v akci.
Informace o tom, jak nasadit webovou aplikaci, najdete v tématu Hostování a nasazení ASP.NET Core.
Informace o dalších tématech souvisejících s ASP.NET Core MVC, jako je ověřování a autorizace, najdete v tématu Přehled ASP.NET Core.
Pokyny k vytvoření spolehlivé, zabezpečené, výkonné, testovatelné a škálovatelné aplikace ASP.NET Core najdete v vzorech podnikových webových aplikací. K dispozici je kompletní ukázková webová aplikace pro produkční kvalitu, která implementuje vzory.
Další kroky
V tomto kurzu:
- Provedené nezpracované dotazy SQL
- Byl spuštěn dotaz k vrácení entit.
- Byl vyvolán dotaz pro vrácení dalších typů.
- Říká se tomu aktualizační dotaz.
- Prozkoumání dotazů SQL
- Vytvoření abstraktní vrstvy
- Dozvěděli jste se o automatické detekci změn
- Dozvěděli jste se o EF Core zdrojových kódech a plánech vývoje
- Naučili jste se používat dynamické LINQ ke zjednodušení kódu.
Tím se dokončí tato série kurzů o používání Entity Framework Core v aplikaci ASP.NET Core MVC. Tato série pracovala s novou databází; alternativou je zpětně analyzovat model z existující databáze.
Kurz : EF Core s MVC, existující databáze