Kurz: Začínáme s EF Core ve webové aplikaci ASP.NET MVC

Tom Dykstra a Rick Anderson

V tomto kurzu se naučíte ASP.NET Core MVC a Entity Framework Core s kontrolery a zobrazeními. Razor Pages je alternativní programovací model. Pro nový vývoj doporučujeme Razor stránky přes MVC s kontrolery a zobrazeními. Podívejte se na Razor verzi stránky tohoto kurzu. Každý kurz se zabývá některými materiály, které ostatní nemají:

Některé věci, které tento kurz MVC obsahuje, že Razor kurz Pages:

  • Implementace dědičnosti v datovém modelu
  • Provádění nezpracovaných dotazů SQL
  • Zjednodušení kódu pomocí dynamické technologie LINQ

Některé věci, které Razor kurz Pages obsahuje, že tento kurz neobsahuje:

  • Načtení souvisejících dat pomocí metody Select
  • Osvědčené postupy pro EF.

Tento kurz nebyl aktualizován pro ASP.NET Core 6. Webové šablony ASP.NET Core 6 používají nový minimální model hostování, který se sjednocuje Startup.cs s Program.cs jedním Program.cs souborem. Dokud se tento kurz neaktualizuje, podívejte Razor se na stránky s Entity Framework Core v ASP.NET Core – kurz 1 z 8 a části 4, přidejte model do aplikace ASP.NET Core MVC, jak používat EF s novým minimálním hostitelským modelem. Aktualizace kurzu pro ASP.NET Core 6 je sledována v tomto GitHub problému.

Ukázková webová aplikace Contoso University ukazuje, jak vytvořit webovou aplikaci ASP.NET Core MVC pomocí Entity Frameworku (EF) Core a Visual Studio.

Ukázková aplikace je web pro fiktivní contososkou univerzitu. Zahrnuje funkce, jako je přijetí studentů, vytváření kurzů a zadání instruktora. Toto je první z řady kurzů, které vysvětlují, jak sestavit ukázkovou aplikaci Contoso University.

Požadavky

Databázové stroje

Pokyny Visual Studio používají SQL Server LocalDB, verzi SQL Server Express, která běží jenom na Windows.

Řešení problémů a řešení potíží

Pokud narazíte na problém, který nemůžete vyřešit, můžete řešení obecně najít porovnáním kódu s dokončeným projektem. Seznam běžných chyb a jejich řešení najdete v části Řešení potíží v posledním kurzu série. Pokud nenajdete, co tam potřebujete, můžete zadat dotaz, který vám StackOverflow.com pro ASP.NET Core nebo EF Core.

Tip

Toto je série 10 kurzů, z nichž každá vychází z toho, co se provádí v předchozích kurzech. Po dokončení každého úspěšného kurzu zvažte uložení kopie projektu. Pokud narazíte na problémy, můžete začít znovu z předchozího kurzu a nemusíte se vrátit na začátek celé série.

Webová aplikace Contoso University

Aplikace integrovaná v těchto kurzech je základní web pro vysokoškoláky.

Uživatelé můžou zobrazit a aktualizovat informace o studentech, kurzech a instruktorech. Tady je několik obrazovek v aplikaci:

Students Index page

Students Edit page

Vytvoření webové aplikace

  1. Spusťte Visual Studio a vyberte Vytvořit nový projekt.
  2. V dialogovém okně Vytvořit nový projekt vyberte ASP.NET Core WebApplicationNext>.
  3. V dialogovém okně Konfigurovat nový projekt zadejte ContosoUniversitynázev Project. Je důležité použít tento přesný název včetně velká písmena, takže každý z nich namespace odpovídá při zkopírování kódu.
  4. Vyberte Vytvořit.
  5. V dialogovém okně Vytvořit novou ASP.NET Core webové aplikace vyberte:
    1. .NET Core a ASP.NET Core 5.0 v rozevíracích sadě.
    2. ASP.NET Core Web App (Model-View-Controller).
    3. VytvořitNew ASP.NET Core Project dialog

Nastavení stylu webu

Několik základních změn nastaví nabídku webu, rozložení a domovskou stránku.

Otevřete Views/Shared/_Layout.cshtml a proveďte následující změny:

  • Změňte každý výskyt ContosoUniversity na Contoso University. Existují tři výskyty.
  • Přidejte položky nabídky Pro, Studenty, Kurzy, Instruktory a Oddělení a odstraňte Privacy položku nabídky.

Předchozí změny jsou zvýrazněné v následujícím kódu:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

Nahraďte Views/Home/Index.cshtmlobsah souboru následujícím kódem:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p><a class="btn btn-default" href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-mvc/intro/samples/5cu-final">See project source code &raquo;</a></p>
    </div>
</div>

Stisknutím kombinace kláves CTRL+F5 spusťte projekt nebo v nabídce zvolte > Spustit ladění bez ladění. Domovská stránka se zobrazí s kartami stránek vytvořených v tomto kurzu.

Contoso University home page

Balíčky NuGet EF Core

Tento kurz používá SQL Server a balíček zprostředkovatele je Microsoft.EntityFrameworkCore.SqlServer.

Balíček EF SQL Server a jeho závislosti Microsoft.EntityFrameworkCore a Microsoft.EntityFrameworkCore.Relationalposkytuje podporu modulu runtime ef.

Přidejte balíček microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet. V konzole Správce balíčků (PMC) zadejte následující příkazy pro přidání NuGet balíčků:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer

Balíček Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet poskytuje ASP.NET Core middleware pro chybové stránky EF Core. Tento middleware pomáhá zjišťovat a diagnostikovat chyby při migracích EF Core.

Informace o jiných poskytovatelech databází, kteří jsou k dispozici pro EF Core, naleznete v tématu Poskytovatelé databáze.

Vytvoření datového modelu

Pro tuto aplikaci se vytvoří následující třídy entit:

Course-Enrollment-Student data model diagram

Předchozí entity mají následující relace:

  • Vztah 1:N mezi Student entitami a Enrollment entitami. Student se může zaregistrovat v libovolném počtu kurzů.
  • Vztah 1:N mezi Course entitami a Enrollment entitami. Kurz může mít v něm zaregistrovaný libovolný počet studentů.

V následujících částech se pro každou z těchto entit vytvoří třída.

Entita Student

Student entity diagram

Ve složce Models vytvořte Student třídu s následujícím kódem:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Vlastnost ID je sloupec primárního klíče (PK) databázové tabulky, která odpovídá této třídě. Ef ve výchozím nastavení interpretuje vlastnost, která je pojmenovaná ID nebo classnameID jako primární klíč. Pk může být například pojmenována StudentID místo ID.

Vlastnost Enrollments je navigační vlastnost. Vlastnosti navigace obsahují další entity, které souvisejí s touto entitou. Vlastnost EnrollmentsStudent entity:

  • Obsahuje všechny Enrollment entity, které souvisejí s danou Student entitou.
  • Pokud má konkrétní Student řádek v databázi dva související Enrollment řádky:
    • Student Navigační vlastnost dané entity Enrollments obsahuje tyto dvě Enrollment entity.

Enrollment řádky obsahují hodnotu PK studenta ve sloupci cizího StudentID klíče (FK).

Pokud navigační vlastnost může obsahovat více entit:

  • Typ musí být seznam, například ICollection<T>, List<T>nebo HashSet<T>.
  • Entity je možné přidat, odstranit a aktualizovat.

Relace navigace M:N a 1:N můžou obsahovat více entit. Při ICollection<T> použití ef vytvoří HashSet<T> kolekci ve výchozím nastavení.

Entita Registrace

Enrollment entity diagram

Ve složce Models vytvořte Enrollment třídu s následujícím kódem:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

Vlastnost EnrollmentID je PK. Tato entita classnameID místo sebe používá vzor ID . Entita Student použila ID vzor. Někteří vývojáři preferují použití jednoho vzoru v celém datovém modelu. V tomto kurzu ukazuje varianta, že lze použít některý ze vzorů. V pozdějším kurzu se dozvíte, jak použití ID bez názvu třídy usnadňuje implementaci dědičnosti v datovém modelu.

Vlastnost Grade je enum. The ? after the Grade type declarations indikuje, že Grade vlastnost je nullable. Známka, která se null liší od nulové známky. null znamená, že známka není známa nebo ještě nebyla přiřazena.

Vlastnost StudentID je cizí klíč (FK) a odpovídající navigační vlastnost je Student. Entita Enrollment je přidružená k jedné Student entitě, takže vlastnost může obsahovat pouze jednu Student entitu. To se liší od Student.Enrollments navigační vlastnosti, která může obsahovat více Enrollment entit.

Vlastnost CourseID je FK a odpovídající navigační vlastnost je Course. Entita Enrollment je přidružená k jedné Course entitě.

Entity Framework interpretuje vlastnost jako vlastnost FK, pokud se jmenuje <název> vlastnosti názvu vlastnosti názvu>< navigační vlastnosti. Například pro Student navigační vlastnost, StudentID protože Student pk entity je ID. Vlastnosti FK lze také pojmenovat <název> vlastnosti primárního klíče. Například proto, CourseID že Course pk entity je CourseID.

Entita Kurzu

Course entity diagram

Ve složce Models vytvořte Course třídu s následujícím kódem:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Vlastnost Enrollments je navigační vlastnost. Entita Course může souviset s libovolným Enrollment počtem entit.

Atribut DatabaseGenerated je vysvětlený v pozdějším kurzu. Tento atribut umožňuje zadat PK pro kurz, místo aby ji databáze vygenerovala.

Vytvoření kontextu databáze

Hlavní třída, která koordinuje funkce EF pro daný datový model, je DbContext třída kontextu databáze. Tato třída je vytvořena odvozením z Microsoft.EntityFrameworkCore.DbContext třídy. Odvozená DbContext třída určuje, které entity jsou součástí datového modelu. Některé chování EF je možné přizpůsobit. V tomto projektu má třída název SchoolContext.

Ve složce projektu vytvořte složku s názvem Data.

Ve složce Data vytvořte SchoolContext třídu s následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }
    }
}

Předchozí kód vytvoří DbSet vlastnost pro každou sadu entit. V terminologii EF:

  • Sada entit obvykle odpovídá databázové tabulce.
  • Entita odpovídá řádku v tabulce.

Příkazy DbSet<Enrollment> a DbSet<Course> příkazy by mohly být vynechány a fungovaly by stejně. Ef by je zahrnoval implicitně, protože:

  • Entita Student odkazuje na entitu Enrollment .
  • Entita Enrollment odkazuje na entitu Course .

Při vytvoření databáze ef vytvoří tabulky, které mají názvy stejné jako DbSet názvy vlastností. Názvy vlastností pro kolekce jsou obvykle množné číslo. Například místo StudentsStudent. Vývojáři nesouhlasí s tím, jestli mají být názvy tabulek v množném čísle nebo ne. V těchto kurzech se výchozí chování přepíše zadáním názvů tabulek s jednotným číslem v .DbContext Uděláte to tak, že za poslední vlastnost DbSet přidáte následující zvýrazněný kód.

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

Zaregistrujte SchoolContext

ASP.NET Core zahrnuje injektáž závislostí. Služby, jako je kontext databáze EF, se během spouštění aplikace registrují pomocí injektáže závislostí. Komponenty, které vyžadují tyto služby, například kontrolery MVC, jsou tyto služby poskytovány prostřednictvím parametrů konstruktoru. Kód konstruktoru kontroleru, který získá instanci kontextu, se zobrazí dále v tomto kurzu.

Pokud se chcete zaregistrovat SchoolContext jako služba, otevřete Startup.csa přidejte do metody zvýrazněné řádky ConfigureServices .

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ContosoUniversity
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<SchoolContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddControllersWithViews();
        }

Název připojovacího řetězce se předá kontextu voláním metody objektu DbContextOptionsBuilder . Pro místní vývoj ASP.NET Core konfigurační systém načte připojovací řetězec ze appsettings.json souboru.

appsettings.json Otevřete soubor a přidejte připojovací řetězec, jak je znázorněno v následujícím kódu:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Přidání filtru výjimek databáze

ConfigureServices Přidejte AddDatabaseDeveloperPageExceptionFilter ho, jak je znázorněno v následujícím kódu:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddDatabaseDeveloperPageExceptionFilter();

    services.AddControllersWithViews();
}

Poskytuje AddDatabaseDeveloperPageExceptionFilter užitečné informace o chybě ve vývojovém prostředí.

SQL Server Express LocalDB

Připojovací řetězec určuje SQL Server LocalDB. LocalDB je zjednodušená verze databázového stroje SQL Server Express a je určená pro vývoj aplikací, nikoli pro produkční použití. LocalDB se spouští na vyžádání a spouští se v uživatelském režimu, takže neexistuje složitá konfigurace. LocalDB ve výchozím nastavení vytvoří v C:/Users/<user> adresáři soubory .mdf DB.

Inicializace databáze s testovacími daty

EF vytvoří prázdnou databázi. V této části se přidá metoda, která je volána po vytvoření databáze, aby se naplnila testovacími daty.

Metoda EnsureCreated se používá k automatickému vytvoření databáze. V pozdějším kurzu se dozvíte, jak zpracovávat změny modelu pomocí Migrace Code First ke změně schématu databáze místo vyřazení a opětovného vytvoření databáze.

Ve složce Data vytvořte novou třídu s názvem DbInitializer s následujícím kódem:

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
            new Course{CourseID=1050,Title="Chemistry",Credits=3},
            new Course{CourseID=4022,Title="Microeconomics",Credits=3},
            new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
            new Course{CourseID=1045,Title="Calculus",Credits=4},
            new Course{CourseID=3141,Title="Trigonometry",Credits=4},
            new Course{CourseID=2021,Title="Composition",Credits=3},
            new Course{CourseID=2042,Title="Literature",Credits=4}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}

Předchozí kód zkontroluje, jestli databáze existuje:

  • Pokud databáze nebyla nalezena;
    • Vytvoří se a načte s testovacími daty. Načte testovací data do polí, nikoli List<T> do kolekcí za účelem optimalizace výkonu.
  • Pokud se databáze najde, neprobere to žádnou akci.

Aktualizujte Program.cs následujícím kódem:

using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Program.cs provede při spuštění aplikace následující:

  • Získejte instanci kontextu databáze z kontejneru injektáže závislostí.
  • Zavolejte metodu DbInitializer.Initialize .
  • Při dokončení metody odstraňte kontext Initialize , jak je znázorněno v následujícím kódu:
public static void Main(string[] args)
{
     var host = CreateWebHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<SchoolContext>();
            DbInitializer.Initialize(context);
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred while seeding the database.");
        }
    }

    host.Run();
}

Při prvním spuštění aplikace se databáze vytvoří a načte s testovacími daty. Kdykoli se datový model změní:

  • Odstraňte databázi.
  • Aktualizujte počáteční metodu a spusťte afresh s novou databází.

V dalších kurzech se databáze upraví při změně datového modelu bez odstranění a opětovného vytvoření. Při změně datového modelu se neztratí žádná data.

Vytvoření kontroleru a zobrazení

Pomocí modulu generování uživatelského rozhraní v Visual Studio přidejte kontroler MVC a zobrazení, která budou používat EF k dotazování a ukládání dat.

Automatické vytváření metod akcí CRUD a zobrazení se označuje jako generování.

  • V Průzkumník řešení klikněte pravým tlačítkem na Controllers složku a vyberte Přidat > novou vygenerovanou položku.
  • V dialogovém okně Přidat generování uživatelského rozhraní :
    • Vyberte kontroler MVC se zobrazeními pomocí Entity Frameworku.
    • Klikněte na Přidat. Zobrazí se dialogové okno Přidat kontroler MVC se zobrazením pomocí entity Framework : Scaffold Student
    • Ve třídě Model vyberte Student.
    • V kontextové třídě data vyberte SchoolContext.
    • Jako název přijměte výchozí StudentsController .
    • Klikněte na Přidat.

Modul pro generování uživatelského rozhraní Visual Studio vytvoří StudentsController.cs soubor a sadu zobrazení (*.cshtmlsouborů), které pracují s kontrolerem.

Všimněte si, že kontroler přebírá SchoolContext jako parametr konstruktoru.

namespace ContosoUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

ASP.NET Core injektáž závislostí se postará o předání instance SchoolContext kontroleru. Nakonfigurovali jste ji ve Startup třídě.

Kontroler obsahuje metodu Index akce, která zobrazuje všechny studenty v databázi. Metoda získá seznam studentů z entity Students nastavenou čtením Students vlastnosti instance kontextu databáze:

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}

Asynchronní programovací prvky v tomto kódu jsou vysvětleny později v kurzu.

Zobrazení Views/Students/Index.cshtml zobrazí tento seznam v tabulce:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    @Html.DisplayNameFor(model => model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.EnrollmentDate)
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Stisknutím kombinace kláves CTRL+F5 spusťte projekt nebo v nabídce zvolte Ladit > spustit bez ladění .

Kliknutím na kartu Studenti zobrazíte testovací data vložená metodou DbInitializer.Initialize . V závislosti na tom, jak je okno prohlížeče úzké, uvidíte Students odkaz na kartu v horní části stránky nebo budete muset kliknout na ikonu navigace v pravém horním rohu, abyste viděli odkaz.

Contoso University home page narrow

Students Index page

Zobrazení databáze

Po spuštění DbInitializer.Initialize aplikace volá EnsureCreatedmetoda . EF viděla, že neexistuje žádná databáze:

  • Proto vytvořila databázi.
  • Kód Initialize metody naplní databázi daty.

K zobrazení databáze v Visual Studio použijte SQL Server Průzkumník objektů (SSOX):

  • V nabídce Zobrazení v Visual Studio vyberte SQL Server Průzkumník objektů.
  • V SSOX vyberte (localdb)\MSSQLLocalDB > Databases.
  • Vyberte ContosoUniversity1položku pro název databáze, která je v připojovacím řetězci v appsettings.json souboru.
  • Rozbalte uzel Tabulky a zobrazte tabulky v databázi.

Tables in SSOX

Klikněte pravým tlačítkem myši na tabulku Student a kliknutím na Zobrazit data zobrazte data v tabulce.

Student table in SSOX

Soubory *.mdf a *.ldf soubory databáze jsou ve složce C:\Users\<username> .

Protože EnsureCreated se volá v inicializační metodě, která běží na spuštění aplikace, můžete:

  • Proveďte změnu třídy Student .
  • Odstraňte databázi.
  • Zastavte a spusťte aplikaci. Databáze se automaticky znovu vytvoří tak, aby odpovídala změně.

Pokud je například vlastnost EmailAddress přidána do Student třídy, nový EmailAddress sloupec v znovu vytvořené tabulce. Zobrazení nezobrazí novou EmailAddress vlastnost.

Konvence

Množství kódu napsaného pro vytvoření úplné databáze EF je minimální kvůli použití konvencí, které ef používá:

  • Názvy DbSet vlastností se používají jako názvy tabulek. U entit, na které vlastnost odkazuje DbSet , se názvy tříd entit používají jako názvy tabulek.
  • Názvy vlastností entity se používají pro názvy sloupců.
  • Vlastnosti entity, které jsou pojmenované ID nebo classnameID jsou rozpoznány jako vlastnosti PK.
  • Vlastnost se interpretuje jako vlastnost FK, pokud se jmenuje název <vlastnosti> namePK>< navigace. Například pro Student navigační vlastnost, StudentID protože Student pk entity je ID. Vlastnosti FK mohou být také pojmenované název <>vlastnosti primárního klíče. Například vzhledem k tomu, EnrollmentID že Enrollment infrastruktura veřejných klíčů entity je EnrollmentID.

Konvenční chování lze přepsat. Například názvy tabulek lze explicitně zadat, jak je znázorněno výše v tomto kurzu. Názvy sloupců a libovolná vlastnost lze nastavit jako PK nebo FK.

Asynchronní kód

Asynchronní programování je výchozí režim pro ASP.NET Core a EF Core.

Webový server má k dispozici omezený počet vláken a v situacích s vysokým zatížením se můžou používat všechna dostupná vlákna. Když k tomu dojde, server nemůže zpracovat nové požadavky, dokud se vlákna nevysadí. Při synchronním kódu může být mnoho vláken svázané, zatímco ve skutečnosti neprovádí žádnou práci, protože čekají na dokončení vstupně-výstupních operací. Při asynchronním kódu, když proces čeká na dokončení vstupně-výstupních operací, jeho vlákno se uvolní, aby server používal ke zpracování dalších požadavků. V důsledku toho asynchronní kód umožňuje efektivnější použití prostředků serveru a server je povolen pro zpracování většího provozu bez zpoždění.

Asynchronní kód zavádí v době běhu malou režii, ale u situací s nízkým provozem je dosažení výkonu zanedbatelné, zatímco u situací s vysokým provozem je potenciální zlepšení výkonu podstatné.

V následujícím kódu , async, Task<T>awaita ToListAsync proveďte kód asynchronně.

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • Klíčové async slovo říká kompilátoru, aby vygeneroval zpětné volání pro části textu metody a automaticky vytvořil vrácený Task<IActionResult> objekt.
  • Návratový typ Task<IActionResult> představuje probíhající práci s výsledkem typu IActionResult.
  • await Klíčové slovo způsobí, že kompilátor rozdělí metodu na dvě části. První část končí operací, která se spouští asynchronně. Druhá část se vloží do metody zpětného volání, která se volá po dokončení operace.
  • ToListAsync je asynchronní verze ToList metody rozšíření.

Některé věci, které je potřeba vědět při psaní asynchronního kódu, který používá EF:

  • Asynchronně se spustí pouze příkazy, které způsobují odeslání dotazů nebo příkazů do databáze. To zahrnuje například ToListAsync, , a SingleOrDefaultAsyncSaveChangesAsync. Nezahrnuje například příkazy, které jenom mění IQueryable, například var students = context.Students.Where(s => s.LastName == "Davolio").
  • Kontext EF není bezpečný pro vlákno: Nepokoušejte se paralelně provádět více operací. Při volání jakékoli asynchronní metody EF vždy použijte await klíčové slovo.
  • Pokud chcete využít výhod výkonu asynchronního kódu, ujistěte se, že všechny balíčky knihoven použité také používají asynchronní, pokud volají nějaké metody EF, které způsobují odeslání dotazů do databáze.

Další informace o asynchronním programování v .NET najdete v tématu Přehled asynchronního programování.

Omezení načtených entit

Informace o omezení počtu entit vrácených z dotazu najdete v tématu Důležité informace o výkonu .

protokolování entity Framework Core SQL

Konfigurace protokolování se běžně poskytuje v Logging části appsettings.{Environment}.json souborů. Pokud chcete protokolovat příkazy SQL, přidejte "Microsoft.EntityFrameworkCore.Database.Command": "Information" je appsettings.Development.json do souboru:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
     ,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "AllowedHosts": "*"
}

S předchozím kódem JSON se příkazy SQL zobrazí na příkazovém řádku a v okně výstupu Visual Studio.

Další informace najdete v tématu Protokolování v .NET Core a ASP.NET Core a tento problém GitHub.

V dalším kurzu se dozvíte, jak provádět základní operace CRUD (vytváření, čtení, aktualizace, odstranění).

V tomto kurzu se naučíte ASP.NET Core MVC a Entity Framework Core s řadiči a zobrazeními. Razor Pages je alternativní programovací model. Pro nový vývoj doporučujeme Razor stránky přes MVC s řadiči a zobrazeními. Podívejte se na Razor verzi Stránky tohoto kurzu. Každý kurz se zabývá některými materiály, které ostatní ne:

Některé věci, které tento kurz MVC obsahuje, že Razor kurz Pages:

  • Implementace dědičnosti v datovém modelu
  • Provádění nezpracovaných dotazů SQL
  • Zjednodušení kódu pomocí dynamického LINQ

Některé věci, Razor které kurz Pages obsahuje, že to není:

  • Načtení souvisejících dat pomocí metody Select
  • Osvědčené postupy pro EF.

Ukázková webová aplikace Contoso University ukazuje, jak vytvořit webové aplikace ASP.NET Core 2.2 MVC pomocí Entity Frameworku (EF) Core 2.2 a Visual Studio 2017 nebo 2019.

Tento kurz nebyl aktualizován pro ASP.NET Core 3.1. Byla aktualizována pro ASP.NET Core 5.0.

Ukázková aplikace je web fiktivní univerzity Contoso. Zahrnuje funkce, jako je přijetí studentů, vytváření kurzů a zadání instruktora. Toto je první v řadě kurzů, které vysvětlují, jak vytvořit ukázkovou aplikaci Contoso University úplně od začátku.

Požadavky

Řešení potíží

Pokud narazíte na problém, který nemůžete vyřešit, můžete řešení obecně najít porovnáním kódu s dokončeným projektem. Seznam běžných chyb a jejich řešení najdete v části Řešení potíží v posledním kurzu série. Pokud nenajdete, co tam potřebujete, můžete zadat dotaz, který vám StackOverflow.com pro ASP.NET Core nebo EF Core.

Tip

Toto je série 10 kurzů, z nichž každá vychází z toho, co se provádí v předchozích kurzech. Po dokončení každého úspěšného kurzu zvažte uložení kopie projektu. Pokud narazíte na problémy, můžete začít znovu z předchozího kurzu a nemusíte se vrátit na začátek celé série.

Webová aplikace Contoso University

Aplikace, kterou budete v těchto kurzech vytvářet, je jednoduchý webový web pro vysokoškoláky.

Uživatelé můžou zobrazit a aktualizovat informace o studentech, kurzech a instruktorech. Tady je několik obrazovek, které vytvoříte.

Students Index page

Students Edit page

Vytvoření webové aplikace

  • Otevřete sadu Visual Studio.

  • V nabídce Soubor vyberte Nový > Project.

  • V levém podokně vyberte Nainstalovaný > web Visual C#>.

  • Vyberte šablonu projektu webové aplikace ASP.NET Core.

  • Jako název zadejte ContosoUniversity a klikněte na TLAČÍTKO OK.

    New Project dialog

  • Počkejte, až se zobrazí dialogové okno Nová webová aplikace ASP.NET Core.

  • Vyberte .NET Core, ASP.NET Core 2.2 a šablonu Webové aplikace (Model-View-Controller).

  • Ujistěte se, že je ověřování nastavené na Žádné ověřování.

  • Vyberte OK.

    New ASP.NET Core Project dialog

Nastavení stylu webu

Několik jednoduchých změn nastaví nabídku webu, rozložení a domovskou stránku.

Otevřete Views/Shared/_Layout.cshtml a proveďte následující změny:

  • Změňte každý výskyt "ContosoUniversity" na Contoso University. Existují tři výskyty.

  • Přidejte položky nabídky Pro, Studenty, Kurzy, Instruktory a Oddělení a odstraňte Privacy položku nabídky.

Změny jsou zvýrazněné.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
              crossorigin="anonymous"
              integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE="/>
    </environment>
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <partial name="_CookieConsentPartial" />
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2019 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha256-E/V4cWE4qvAeO5MOhjtGtqDzPndRO1LBk8lJ/PR7CA4=">
        </script>
    </environment>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>

Nahraďte Views/Home/Index.cshtmlobsah souboru následujícím kódem, který nahradí text o ASP.NET a MVC textem o této aplikaci:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p><a class="btn btn-default" href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code &raquo;</a></p>
    </div>
</div>

Stisknutím kombinace kláves CTRL+F5 spusťte projekt nebo v nabídce zvolte > Spustit ladění bez ladění. Zobrazí se domovská stránka s kartami pro stránky, které vytvoříte v těchto kurzech.

Contoso University home page

Informace o balíčcích EF Core NuGet

Pokud chcete do projektu přidat podporu EF Core, nainstalujte zprostředkovatele databáze, kterého chcete cílit. Tento kurz používá SQL Server a balíček zprostředkovatele je Microsoft.EntityFrameworkCore.SqlServer. Tento balíček je součástí Microsoft.AspNetCore.App metabalíku, takže na balíček nemusíte odkazovat.

Balíček EF SQL Server a jeho závislosti (Microsoft.EntityFrameworkCoreaMicrosoft.EntityFrameworkCore.Relational) poskytují podporu modulu runtime ef. Balíček nástrojů přidáte později v kurzu Migrace .

Informace o jiných zprostředkovatelech databáze, kteří jsou k dispozici pro Entity Framework Core, naleznete v tématu Zprostředkovatelé databáze.

Vytvoření datového modelu

Dále vytvoříte třídy entit pro aplikaci Contoso University. Začnete následujícími třemi entitami.

Course-Enrollment-Student data model diagram

Mezi entitami Enrollment existuje vztah Student 1:N a mezi entitami existuje vztah CourseEnrollment 1:N. Jinými slovy, student může být zaregistrovaný v libovolném počtu kurzů a předmět může mít v něm libovolný počet studentů.

V následujících částech vytvoříte třídu pro každou z těchto entit.

Entita Student

Student entity diagram

Ve složce Models vytvořte soubor třídy s názvem Student.cs a nahraďte kód šablony následujícím kódem.

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Vlastnost ID se stane primárním klíčem sloupce databázové tabulky, která odpovídá této třídě. Ve výchozím nastavení interpretuje Entity Framework vlastnost s názvem ID nebo classnameID jako primární klíč.

Vlastnost Enrollments je navigační vlastnost. Vlastnosti navigace obsahují další entity, které souvisejí s touto entitou. V tomto případě Enrollments bude vlastnost Student entity obsahovat všechny Enrollment entity, které souvisejí s danou Student entitou. Jinými slovy, pokud Student má řádek v databázi dva související Enrollment řádky (řádky obsahující hodnotu primárního klíče studenta ve sloupci cizího klíče StudentID), Student bude navigační vlastnost dané entity Enrollments obsahovat tyto dvě Enrollment entity.

Pokud navigační vlastnost může obsahovat více entit (jako v relacích M:N nebo 1:N), musí být jeho typ seznamem, ve kterém lze položky přidávat, odstraňovat a aktualizovat, například ICollection<T>. Můžete zadat ICollection<T> nebo zadat typ, například List<T> nebo HashSet<T>. Pokud zadáte ICollection<T>, ef ve výchozím nastavení vytvoří kolekci HashSet<T> .

Entita Registrace

Enrollment entity diagram

Ve složce Models vytvořte Enrollment.cs a nahraďte existující kód následujícím kódem:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

Tato vlastnost bude primárním klíčem. Tato EnrollmentID entita místo sebe používá classnameID vzor ID , jak jste viděli v entitě Student . Obvykle byste zvolili jeden vzor a použili ho v celém datovém modelu. V této variantě je vidět, že můžete použít některý ze vzorů. V pozdějším kurzu se dozvíte, jak použití ID bez názvu třídy usnadňuje implementaci dědičnosti v datovém modelu.

Vlastnost Grade je enum. Otazník za Grade deklaraci typu označuje, že Grade vlastnost je null. Známka, která má hodnotu null, se liší od nulové známky – hodnota null znamená, že známka není známa nebo ještě nebyla přiřazena.

Vlastnost StudentID je cizí klíč a odpovídající navigační vlastnost je Student. Entita Enrollment je přidružená k jedné Student entitě, takže vlastnost může obsahovat pouze jednu Student entitu (na rozdíl od Student.Enrollments navigační vlastnosti, kterou jste viděli dříve, což může obsahovat více Enrollment entit).

Vlastnost CourseID je cizí klíč a odpovídající navigační vlastnost je Course. Entita Enrollment je přidružená k jedné Course entitě.

Entity Framework interpretuje vlastnost jako vlastnost cizího klíče, pokud je pojmenovaná <navigation property name><primary key property name> (například StudentID pro Student navigační vlastnost, protože Student primární klíč entity je ID). Vlastnosti cizího klíče lze také pojmenovat jednoduše <primary key property name> (například proto, CourseID že Course primární klíč entity je CourseID).

Entita Kurzu

Course entity diagram

Ve složce Models vytvořte Course.cs a nahraďte existující kód následujícím kódem:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Vlastnost Enrollments je navigační vlastnost. Entita Course může souviset s libovolným Enrollment počtem entit.

O atributu DatabaseGenerated se dozvíme více v pozdějším kurzu této série. V podstatě tento atribut umožňuje zadat primární klíč pro kurz místo toho, abyste databázi vygenerovala.

Vytvoření kontextu databáze

Hlavní třída, která koordinuje funkce Entity Framework pro daný datový model, je třída kontextu databáze. Tuto třídu vytvoříte odvozením z Microsoft.EntityFrameworkCore.DbContext třídy. V kódu určíte, které entity jsou součástí datového modelu. Můžete také přizpůsobit určité chování Entity Frameworku. V tomto projektu má třída název SchoolContext.

Ve složce projektu vytvořte složku s názvem Data.

Ve složce Data vytvořte nový soubor třídy s názvem SchoolContext.csa nahraďte kód šablony následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }
    }
}

Tento kód vytvoří DbSet vlastnost pro každou sadu entit. V terminologii Entity Framework sada entit obvykle odpovídá databázové tabulce a entita odpovídá řádku v tabulce.

Mohli jste vynechat DbSet<Enrollment> a DbSet<Course> příkazy a fungovalo by to stejně. Entity Framework by je implicitně zahrnovala, protože entita Student odkazuje na entitu Enrollment a entita Enrollment odkazuje na entitu Course .

Po vytvoření databáze ef vytvoří tabulky, které mají stejné názvy jako DbSet názvy vlastností. Názvy vlastností pro kolekce jsou obvykle množné číslo (Studenti místo studenta), ale vývojáři nesouhlasí s tím, jestli by názvy tabulek měly být množné nebo ne. Pro tyto kurzy přepíšete výchozí chování zadáním jedinečných názvů tabulek v DbContext. Uděláte to tak, že za poslední vlastnost DbSet přidáte následující zvýrazněný kód.

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

Sestavte projekt jako kontrolu chyb kompilátoru.

Registrace schoolContextu

ASP.NET Core implementuje injektáž závislostí ve výchozím nastavení. Služby (například kontext databáze EF) se při spuštění aplikace registrují pomocí injektáže závislostí. Komponenty, které vyžadují tyto služby (například kontrolery MVC), jsou poskytovány těmito službami prostřednictvím parametrů konstruktoru. Uvidíte kód konstruktoru kontroleru, který získá kontextovou instanci později v tomto kurzu.

Pokud se chcete zaregistrovat SchoolContext jako služba, otevřete Startup.csa přidejte do metody zvýrazněné řádky ConfigureServices .

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddMvc();
}

Název připojovacího řetězce se předá kontextu voláním metody objektu DbContextOptionsBuilder . Pro místní vývoj načte konfigurační systém ASP.NET Core připojovací řetězec ze appsettings.json souboru.

Přidejte using příkazy pro ContosoUniversity.Data a Microsoft.EntityFrameworkCore obory názvů a pak sestavte projekt.

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;

appsettings.json Otevřete soubor a přidejte připojovací řetězec, jak je znázorněno v následujícím příkladu.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

SQL Server Express LocalDB

Připojovací řetězec určuje databázi SQL Server LocalDB. LocalDB je zjednodušená verze databázového stroje SQL Server Express a je určená pro vývoj aplikací, nikoli produkční použití. LocalDB začíná na vyžádání a spouští se v uživatelském režimu, takže neexistuje složitá konfigurace. LocalDB ve výchozím nastavení vytvoří v C:/Users/<user> adresáři soubory databáze .mdf.

Inicializace databáze s testovacími daty

Entity Framework pro vás vytvoří prázdnou databázi. V této části napíšete metodu, která se volá po vytvoření databáze, aby byla naplněna testovacími daty.

Tady použijete metodu EnsureCreated k automatickému vytvoření databáze. V pozdějším kurzu se dozvíte, jak zpracovat změny modelu pomocí Migrace Code First a změnit schéma databáze místo vyřazení a opětovného vytvoření databáze.

Ve složce Data vytvořte nový soubor třídy s názvem DbInitializer.cs a nahraďte kód šablony následujícím kódem, který způsobí vytvoření databáze v případě potřeby a načtení testovacích dat do nové databáze.

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
            new Course{CourseID=1050,Title="Chemistry",Credits=3},
            new Course{CourseID=4022,Title="Microeconomics",Credits=3},
            new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
            new Course{CourseID=1045,Title="Calculus",Credits=4},
            new Course{CourseID=3141,Title="Trigonometry",Credits=4},
            new Course{CourseID=2021,Title="Composition",Credits=3},
            new Course{CourseID=2042,Title="Literature",Credits=4}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}

Kód zkontroluje, jestli v databázi nejsou žádní studenti, a pokud ne, předpokládá, že databáze je nová a musí být zasaděna s testovacími daty. Načte testovací data do polí místo List<T> kolekcí pro optimalizaci výkonu.

V Program.cs, upravte metodu Main provést následující při spuštění aplikace:

  • Získejte instanci kontextu databáze z kontejneru injektáže závislostí.
  • Zavolejte počáteční metodu a předejte jí kontext.
  • Při dokončení počáteční metody odstraňte kontext.
using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Při prvním spuštění aplikace se databáze vytvoří a zasadí s testovacími daty. Kdykoli změníte datový model:

  • Odstraňte databázi.
  • Aktualizujte počáteční metodu a spusťte afresh s novou databází stejným způsobem.

V pozdějších kurzech se dozvíte, jak změnit databázi, když se datový model změní, aniž byste ji odstranili a znovu vytvořili.

Vytvoření kontroleru a zobrazení

V této části se modul generování uživatelského rozhraní v Visual Studio používá k přidání kontroleru MVC a zobrazení, která budou používat EF k dotazování a ukládání dat.

Automatické vytváření metod a zobrazení akcí CRUD se označuje jako generování. Generování uživatelského rozhraní se liší od generování kódu v tom, že vygenerovaný kód je výchozím bodem, který můžete upravit tak, aby vyhovoval vašim vlastním požadavkům, zatímco vygenerovaný kód obvykle neupravujete. Když potřebujete upravit vygenerovaný kód, použijete částečné třídy nebo kód znovu vygenerujete při změně.

  • Klikněte pravým tlačítkem myši na složku Kontrolery v Průzkumník řešení a vyberte Přidat > novou položku s generováním uživatelského rozhraní.
  • V dialogovém okně Přidat generování uživatelského rozhraní :
    • Vyberte kontroler MVC se zobrazeními pomocí Entity Frameworku.
    • Klikněte na Přidat. Zobrazí se dialogové okno Přidat kontroler MVC se zobrazením pomocí entity Framework : Scaffold Student
    • Ve třídě modelu vyberte Student.
    • V kontextové třídě data vyberte SchoolContext.
    • Jako název přijměte výchozí StudentsController .
    • Klikněte na Přidat.

Modul pro generování uživatelského rozhraní Visual Studio vytvoří StudentsController.cs soubor a sadu zobrazení (.cshtmlsouborů), které pracují s kontrolerem.

Všimněte si, že kontroler přebírá SchoolContext jako parametr konstruktoru.

namespace ContosoUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

ASP.NET Core injektáž závislostí se postará o předání instance SchoolContext kontroleru. To bylo nakonfigurováno v Startup.cs souboru.

Kontroler obsahuje metodu Index akce, která zobrazuje všechny studenty v databázi. Metoda získá seznam studentů z entity Students nastavenou čtením Students vlastnosti instance kontextu databáze:

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}

Seznámíte se s asynchronními programovacími prvky v tomto kódu dále v tomto kurzu.

Zobrazení Views/Students/Index.cshtml zobrazí tento seznam v tabulce:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    @Html.DisplayNameFor(model => model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.EnrollmentDate)
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Stisknutím kombinace kláves CTRL+F5 spusťte projekt nebo v nabídce zvolte Ladit > spustit bez ladění .

Kliknutím na kartu Studenti zobrazíte testovací data vložená metodou DbInitializer.Initialize . V závislosti na tom, jak je okno prohlížeče úzké, uvidíte Students odkaz na kartu v horní části stránky nebo budete muset kliknout na ikonu navigace v pravém horním rohu, abyste viděli odkaz.

Contoso University home page narrow

Students Index page

Zobrazení databáze

Po spuštění aplikace DbInitializer.Initialize volá EnsureCreatedmetoda . EF viděla, že neexistuje žádná databáze, takže vytvořila jednu, zbytek Initialize kódu metody naplněný databází daty. Databázi můžete zobrazit v Visual Studio pomocí SQL Server Průzkumník objektů (SSOX).

Zavřete prohlížeč.

Pokud okno SSOX ještě není otevřené, vyberte ho v nabídce Zobrazení v Visual Studio.

V SSOX klikněte na (localdb)\MSSQLLocalDB > Databases a potom klikněte na položku pro název databáze, který je v připojovacím řetězci appsettings.json v souboru.

Rozbalte uzel Tabulky a zobrazte tabulky v databázi.

Tables in SSOX

Klikněte pravým tlačítkem myši na tabulku Student a kliknutím na Zobrazit data zobrazte sloupce vytvořené a řádky vložené do tabulky.

Student table in SSOX

Soubory databáze .mdf a .ldf jsou ve složce C:\Users\<username> .

Protože voláte EnsureCreated metodu inicializátoru, která běží na spuštění aplikace, můžete teď provést změnu Student třídy, odstranit databázi, spustit aplikaci znovu a databáze by se automaticky znovu vytvořila tak, aby odpovídala vaší změně. Pokud například přidáte EmailAddress vlastnost do Student třídy, uvidíte v znovu vytvořené tabulce nový EmailAddress sloupec.

Konvence

Množství kódu, který jste museli napsat, aby entity Framework mohl vytvořit úplnou databázi za vás, je minimální kvůli použití konvencí nebo předpokladů, které entity Framework dělá.

  • Názvy DbSet vlastností se používají jako názvy tabulek. U entit, na které vlastnost odkazuje DbSet , se názvy tříd entit používají jako názvy tabulek.
  • Názvy vlastností entity se používají pro názvy sloupců.
  • Vlastnosti entity s názvem ID nebo classnameID jsou rozpoznány jako vlastnosti primárního klíče.
  • Vlastnost se interpretuje jako vlastnost cizího klíče, pokud se jmenuje název <vlastnosti název vlastnosti nameprimary>< navigation key> (například pro Student navigační vlastnost, StudentID protože Student primární klíč entity je ID). Vlastnosti cizího klíče mohou být také pojmenovány jednoduše <název> vlastnosti primárního klíče (například vzhledem k tomu, EnrollmentID že Enrollment primární klíč entity je EnrollmentID).

Konvenční chování lze přepsat. Můžete například explicitně zadat názvy tabulek, jak jste viděli dříve v tomto kurzu. A můžete nastavit názvy sloupců a nastavit libovolnou vlastnost jako primární klíč nebo cizí klíč, jak uvidíte v pozdějším kurzu v této řadě.

Asynchronní kód

Asynchronní programování je výchozí režim pro ASP.NET Core a EF Core.

Webový server má k dispozici omezený počet vláken a v situacích s vysokým zatížením se můžou používat všechna dostupná vlákna. Když k tomu dojde, server nemůže zpracovat nové požadavky, dokud se vlákna nevysadí. Při synchronním kódu může být mnoho vláken svázané, zatímco ve skutečnosti neprovádí žádnou práci, protože čekají na dokončení vstupně-výstupních operací. Při asynchronním kódu, když proces čeká na dokončení vstupně-výstupních operací, jeho vlákno se uvolní, aby server používal ke zpracování dalších požadavků. V důsledku toho asynchronní kód umožňuje efektivnější použití prostředků serveru a server je povolen pro zpracování většího provozu bez zpoždění.

Asynchronní kód zavádí v době běhu malou režii, ale u situací s nízkým provozem je dosažení výkonu zanedbatelné, zatímco u situací s vysokým provozem je potenciální zlepšení výkonu podstatné.

V následujícím kódu async klíčové slovo, Task<T> vrátit hodnotu, await klíčové slovo a ToListAsync metodu, aby se kód spustil asynchronně.

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • Klíčové async slovo říká kompilátoru, aby vygeneroval zpětné volání pro části těla metody a automaticky vytvořil vrácený Task<IActionResult> objekt.
  • Návratový typ Task<IActionResult> představuje probíhající práci s výsledkem typu IActionResult.
  • Klíčové await slovo způsobí, že kompilátor rozdělí metodu do dvou částí. První část končí operací, která se spouští asynchronně. Druhá část se vloží do metody zpětného volání, která se volá po dokončení operace.
  • ToListAsync je asynchronní verze ToList metody rozšíření.

Některé věci, které byste měli vědět při psaní asynchronního kódu, který používá Entity Framework:

  • Asynchronně se spustí pouze příkazy, které způsobují odeslání dotazů nebo příkazů do databáze. To zahrnuje například , ToListAsync, SingleOrDefaultAsynca SaveChangesAsync. Nezahrnuje například příkazy, které právě mění IQueryable, například var students = context.Students.Where(s => s.LastName == "Davolio").
  • Kontext EF není bezpečný pro vlákno: Nepokoušejte se paralelně provádět více operací. Při volání jakékoli asynchronní metody EF vždy použijte await klíčové slovo.
  • Pokud chcete využít výhod výkonu asynchronního kódu, ujistěte se, že všechny balíčky knihovny, které používáte (například pro stránkování), použijte také asynchronní, pokud volají nějaké metody Entity Framework, které způsobují odesílání dotazů do databáze.

Další informace o asynchronním programování v .NET najdete v tématu Přehled asynchronního programování.

Další kroky

V dalším kurzu se dozvíte, jak provádět základní operace CRUD (vytvoření, čtení, aktualizace, odstranění).