Kurz: Informace o pokročilých scénářích – ASP.NET MVC s využitím 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 se naučíte:

  • Provádění nezpracovaných dotazů SQL
  • Volání dotazu pro vrácení entit
  • Volání dotazu pro vrácení 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ých kódech 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. To uděláte tak, že pro vás vygenerujete dotazy a příkazy SQL, které vám také umožní psát je 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. Ve verzi 1.0 máte následující možnosti EF Core :

  • Použijte metodu DbSet.FromSql pro dotazy, které vracejí typy entit. Vrácené objekty musí být typu očekávaného DbSet objektem a jsou automaticky sledovány kontextem databáze, pokud sledování nevypnete.

  • Použijte příkazy Database.ExecuteSqlCommand bez dotazu.

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.

Volání dotazu pro vrá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 Details metodě kontroleru oddělení.

V DepartmentsController.csmetodě Details nahraďte kód, který načte oddělení voláním FromSql metody, 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 potom podrobnosti pro jedno z oddělení.

Department Details

Volání dotazu pro 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 EnrollmentDateGroup objektů modelu zobrazení. 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 EF Core verzi 1.0 je jedním ze způsobů, jak napsat ADO.NET kód a získat připojení k databázi z EF.

V HomeController.cspříkazu 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á předtím udělala.

About page

Volání aktualizačního dotazu

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:

Update Course Credits page

V CoursesController.cs, přidat UpdateCourseCredits metody 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í a ViewData["RowsAffected"]v zobrazení se 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ů.

V Průzkumník řešení klepněte pravým tlačítkem myši na složku Zobrazení/Kurzy a potom klepněte na tlačítko Přidat > novou položku.

V dialogovém okně Přidat novou položku klikněte na ASP.NET Jádro v levém podokně Nainstalované, klikněte na Razor Zobrazit a pojmenujte nové zobrazení UpdateCourseCredits.cshtml.

Nahraďte Views/Courses/UpdateCourseCredits.cshtmlkó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 přidáte "/UpdateCourseCredits" na konec adresy URL v adresním řádku prohlížeče (například: http://localhost:5813/Courses/UpdateCourseCredits). Do textového pole zadejte číslo:

Update Course Credits page

Klikněte na tlačítko Aktualizovat. Zobrazí se počet ovlivněných řádků:

Update Course Credits page rows affected

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[Range(0, 5)] atribut.) 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 se automaticky používají 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 Details metodě nastavte zarážku na if (student == null) příkaz.

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 zobrazující výstup ladění a zobrazí se 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.

Všimněte si, že k získání výstupu protokolování v okně Výstup 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 pracovní třídy pro aktualizace databáze, které používáte ef.

  • EF obsahuje funkce pro implementaci TDD bez psaní kódu úložiště.

Informace o tom, jak implementovat úložiště a jednotku pracovních vzorů, najdete v této sérii kurzů ve verzi Entity Framework 5.

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

  • ChangeTracker.Entries

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 pomocí vlastnosti dočasně vypnete automatickou detekci ChangeTracker.AutoDetectChangesEnabled změn. Příklad:

_context.ChangeTracker.AutoDetectChangesEnabled = false;

EF Core zdrojový kód a plány vývoje

Zdroj Entity Framework Core je na https://github.com/dotnet/efcoreadrese . Úložiště EF Core obsahuje noční buildy, sledování problémů, specifikace funkcí, návrh poznámek ze schůzky a plán pro budoucí vývoj. 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á analýza 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é technologie LINQ

Třetí kurz v této sérii ukazuje, jak psát kód LINQ pomocí pevně kódovacích názvů sloupců v switch příkazu. Se dvěma sloupci, ze kterých si můžete vybrat, to funguje dobře, ale pokud máte mnoho sloupců, může kód získat podrobné. Chcete-li tento problém vyřešit, můžete použít EF.Property metodu k určení názvu vlastnosti jako řetězec. Pokud chcete tento přístup vyzkoušet, nahraďte metodu Index v StudentsController následujícím kódu.

 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));
 }

Poděková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.

Odstraňování běžných chyb

ContosoUniversity.dll používaná jiným procesem

Chybová zpráva:

Nelze otevřít ... bin\Debug\netcoreapp1.0\ContosoUniversity.dll pro zápis – Proces nemá přístup k souboru ...\bin\Debug\netcoreapp1.0\ContosoUniversity.dll, protože ho používá jiný proces.

Řešení:

Zastavte web ve službě IIS Express. Přejděte na hlavním panelu systému Windows, vyhledejte službu IIS Express a klikněte pravým tlačítkem myši na její ikonu, vyberte web Contoso University a potom klikněte na Zastavit web.

Generování migrace bez kódu v metodách nahoru a dolů

Možná příčina:

Příkazy ROZHRANÍ příkazového řádku EF se automaticky nezavírají a neuloží soubory kódu. Pokud máte při spuštění migrations add příkazu neuložené změny, ef změny nenajde.

Řešení:

migrations remove Spusťte příkaz, uložte změny kódu a spusťte migrations add příkaz 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í řetězec 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řejmenovat databázi v appsettings.jsonsouboru . Při příštím spuštění database updatese 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 příkaz Odstranit a 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 rozhraní příkazového database drop řádku:

dotnet ef database drop

Chyba při vyhledání instance SQL Serveru

Chybová zpráva:

Při navazování připojení k SQL Serveru došlo k chybě související se sítí nebo konkrétní instancí. Server se nenašel nebo nebyl dostupný. Ověřte správnost názvu instance. Dále ověřte, jestli je SQL Server nakonfigurovaný tak, aby povoloval vzdálená připojení (Poskytovatel: Síťová rozhraní SQL, chyba: 26 – Chyba při vyhledávání zadaného serveru či instance).

Řešení:

Zkontrolujte připojovací řetězec. Pokud jste soubor databáze odstranili ručně, změňte název databáze v řetězci konstrukce tak, aby začínal novou databází.

Získání kódu

Stáhněte nebo zobrazte dokončenou aplikaci.

Další prostředky

Další informace o EF Coreslužbě Entity Framework Core najdete v dokumentaci. K dispozici je také kniha: Entity Framework Core v akci.

Informace o tom, jak nasadit webovou aplikaci, najdete v tématu Hostitel 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.

Další kroky

V tomto kurzu se naučíte:

  • Provedené nezpracované dotazy SQL
  • Volal se dotaz pro vrácení entit.
  • Volal se dotaz pro vrácení dalších typů.
  • Volal se 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é analýzy modelu z existující databáze.