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 ř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.

Tento kurz nebyl aktualizován pro ASP.NET Core 6. Webové šablony ASP.NET Core 6 používají nový minimální hostitelský model, 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 se službou Entity Framework Core v ASP.NET Core – kurz 1 z 8 a části 4, přidejte model do aplikace ASP.NET Core MVC o tom, jak používat EF s novým minimálním hostitelským modelem. Aktualizace kurzu pro ASP.NET Core 6 se sleduje v tomto problému na GitHubu.

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

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 sestavit ukázkovou aplikaci Contoso University.

Požadavky

Databázové stroje

Pokyny k sadě Visual Studio používají SQL Server LocalDB, verzi SQL Serveru Express, která běží jenom ve 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íží posledního kurzu v řadě. Pokud nenajdete, co tam potřebujete, můžete zadat dotaz na StackOverflow.com pro ASP.NET Core nebo EF Core.

Tip

Toto je řada 10 kurzů, z nichž každá vychází z toho, co se provádí v předchozích kurzech. Zvažte uložení kopie projektu po každém úspěšném dokončení kurzu. Pokud pak narazíte na problémy, můžete začít z předchozího kurzu místo návratu 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 Další webová aplikace> Core.
  3. V dialogovém okně Konfigurovat nový projekt zadejte ContosoUniversitynázev projektu. Je důležité použít tento přesný název včetně velkého písmena, takže každý namespace odpovídá při zkopírování kódu.
  4. Vyberte Vytvořit.
  5. V dialogovém okně Vytvořit novou webovou aplikaci ASP.NET Core 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 informace, 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.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.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

V Views/Home/Index.cshtmlsouboru nahraďte obsah 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 Ladit > spustit bez ladění . Domovská stránka se zobrazí s kartami pro stránky vytvořené v tomto kurzu.

Contoso University home page

Balíčky EF Core NuGet

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

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

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

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

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

Informace o dalších zprostředkovatelů databáze, které jsou k dispozici pro EF Core, najdete 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 může být zaregistrovaný v libovolném počtu kurzů.
  • Vztah 1:N mezi Course entitami a Enrollment entitami. Kurz může mít libovolný počet studentů zaregistrovaných v něm.

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

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) tabulky databáze, 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 pojmenovaná StudentID místo ID.

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

  • Obsahuje všechny entity, které souvisejí s danou EnrollmentStudent entitou.
  • Pokud má konkrétní Student řádek v databázi dva související Enrollment řádky:
    • Vlastnost navigace této Student 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řidávat, odstraňovat a aktualizovat.

Relace navigace M:N a 1:N můžou obsahovat více entit. Při ICollection<T> použití ef vytvoří kolekci HashSet<T> 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 raději používají jeden vzor 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 Poté?, co Grade deklarace typu označuje, že Grade vlastnost je null. Známka, která se null liší od nulové známky. null znamená, že známka ještě 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. Liší se 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 je pojmenovaná název <vlastnosti navigace název><vlastnosti> primárního klíče. 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 proto, CourseID že Course infrastruktura veřejných klíčů 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 toho, 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> by DbSet<Course> 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 .

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. Například Students místo Student. Vývojáři nesouhlasí s tím, jestli by názvy tabulek měly být množné nebo ne. Pro tyto kurzy je výchozí chování přepsáno zadáním jedinečných názvů tabulek v souboru 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");
        }
    }
}

Registrace SchoolContext

ASP.NET Core zahrnuje injektáž závislostí. Služby, jako je 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, poskytují tyto služby prostřednictvím parametrů konstruktoru. Kód konstruktoru kontroleru, který získá kontextovou instanci, se zobrazí 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 .

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 načte konfigurační systém ASP.NET Core 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 tak, 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ách 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 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 .mdf DB.

Inicializace databáze s testovacími daty

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

Metoda EnsureCreated se používá k automatickému vytvoření databáze. V pozdějším kurzu se dozvíte, jak zpracovat změny modelu pomocí migrace Code First, abyste místo vyřazení a opětovného vytvoření databáze změnili schéma 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í místo List<T> kolekcí pro optimalizaci 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. Pokaždé, když se datový model změní:

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

V pozdějších kurzech se databáze upraví, když se datový model změní, aniž by se odstranil a znovu vytvořil. 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 sadě 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 a zobrazení akcí CRUD se označuje jako generování.

  • V PrůzkumníkuControllers řešení klikněte pravým tlačítkem na 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
    • V modelové třídě vyberte Student.
    • V kontextové třídě dat vyberte SchoolContext.
    • Jako název přijměte výchozí StudentsController .
    • Klikněte na Přidat.

Modul generování uživatelského rozhraní sady Visual Studio vytvoří StudentsController.cs soubor a sadu zobrazení (*.cshtml souborů), 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 injektáž závislostí Core 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.

Použití Průzkumníka objektů SQL Serveru (SSOX) k zobrazení databáze v sadě Visual Studio:

  • V nabídce Zobrazení v sadě Visual Studio vyberte Průzkumník objektů SQL Serveru.
  • 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 PK názvu><navigační vlastnosti>. 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í SQL entity Framework Core

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" do appsettings.Development.json 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": "*"
}

Příkazy SQL se zobrazí na JSpříkazovém řádku a v okně výstupu sady Visual Studio.

Další informace najdete v tématu Protokolování v .NET Core a ASP.NET Core a tomto problému s GitHubem.

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 kontrolery 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 Sady 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íží posledního kurzu v řadě. Pokud nenajdete, co tam potřebujete, můžete zadat dotaz na StackOverflow.com pro ASP.NET Core nebo EF Core.

Tip

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

Webová aplikace Contoso University

Aplikace, kterou budete vytvářet v těchto kurzech, je jednoduchý web univerzity.

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ý > projekt.

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

  • Vyberte šablonu projektu ASP.NET Core Web Application .

  • 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 core ASP.NET .

  • 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 informace, 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.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.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.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.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.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.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>

V Views/Home/Index.cshtmlsouboru nahraďte obsah 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 Ladit > spustit bez ladění . Domovskou stránku se zobrazí s kartami stránek, které vytvoříte v těchto kurzech.

Contoso University home page

Informace o balíčcích NUGet EF Core

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í metabalíku Microsoft.AspNetCore.App, takže na balíček nemusíte odkazovat.

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

Informace o dalších zprostředkovatelů databáze, které jsou k dispozici pro Entity Framework Core, naleznete v tématu Poskytovatelé databáze.

Vytvoření datového modelu

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

Course-Enrollment-Student data model diagram

Mezi entitami existuje vztah StudentEnrollment 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 kurz může mít libovolný počet studentů zaregistrovaných v něm.

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 obsahovat všechny Enrollment entity, které souvisejí s danou Student entityStudent entitou. Jinými slovy, pokud Student má řádek v databázi dva související Enrollment řádky (řádky, které obsahují hodnotu primárního klíče studenta ve sloupci cizího klíče StudentID), Student bude tato dvě Enrollment entita obsahovat navigační vlastnost dané entityEnrollments.

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 přidávat, odstraňovat a aktualizovat položky, například ICollection<T>. Můžete zadat nebo zadat ICollection<T> 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 Modely vytvořte Enrollment.cs a nahraďte stávají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; }
    }
}

Vlastnost bude primárním klíčem. Tato EnrollmentID entita používá classnameID vzor místo ID sebe, jak jste viděli v entitě Student . Obvykle byste zvolili jeden vzor a použili ho v celém datovém modelu. Tato varianta ukazuje, ž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 po Grade deklaraci typu označuje, že Grade vlastnost je null. Známka, která má hodnotu null, se liší od nulové úrovně – hodnota null znamená, že známka ještě 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 jenom jednu Student entitu (na rozdíl od Student.Enrollments vlastnosti navigace, 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 vzhledem k tomu, CourseID že Course primární klíč entity je CourseID).

Entita kurzu

Course entity diagram

Ve složce Modely vytvořte Course.cs a nahraďte stávají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 v této sérii. V podstatě tento atribut umožňuje zadat primární klíč kurzu 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 obvykle sada entit 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 Serveru LocalDB. LocalDB je jednoduchá verze databázového stroje SQL Server Express a je určená pro vývoj aplikací, nikoli 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 databázové soubory .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á je volána po vytvoření databáze, aby se naplnila testovacími daty.

V této části použijete metodu EnsureCreated k automatickému vytvoření databáze. V pozdějším kurzu se dozvíte, jak zpracovávat změny modelu pomocí migrací 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 osvědčována testovacími daty. Načte testovací data do polí, nikoli List<T> do kolekcí za účelem optimalizace výkonu.

V Program.cs, upravit 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 vytvoří se 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 dalších kurzech se dozvíte, jak upravit 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 sadě 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 akcí CRUD a zobrazení 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ě.

  • V Průzkumníku řešení klikněte pravým tlačítkem myši na složku Kontrolery 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 generování uživatelského rozhraní sady Visual Studio vytvoří StudentsController.cs soubor a sadu zobrazení (.cshtml souborů), 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 injektáž závislostí jádra 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 později 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 > Spustit ladění 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 v horní části stránky odkaz na kartu nebo budete muset kliknout na ikonu navigace v pravém horním rohu, aby se odkaz zobrazil.

Contoso University home page narrow

Students Index page

Zobrazení databáze

Při spuštění aplikace DbInitializer.Initialize volá metoda EnsureCreated. EF viděl, že neexistuje žádná databáze, a proto ji vytvořila, a pak zbytek Initialize kódu metody naplnil databázi daty. K zobrazení databáze v sadě Visual Studio můžete použít Průzkumník objektů SQL Serveru (SSOX).

Zavřete prohlížeč.

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

V nástroji SSOX klikněte na položku (localdb)\MSSQLLocalDB > Databases a potom klikněte na polož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 sloupce, které byly vytvořeny, a řádky vložené do tabulky.

Student table in SSOX

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

Vzhledem k tomu, ž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 pro vás, je minimální kvůli použití konvencí nebo předpokladů, které entity Framework provádí.

  • DbSet Názvy vlastností se používají jako názvy tabulek. U entit, na DbSet které vlastnost neodkazuje, 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 primárníhoStudent klíče názvu vlastnosti navigace (například StudentID pro navigační vlastnost, protože Student primární klíč entity je ID). Vlastnosti cizího klíče lze také pojmenovat jednoduše <název> vlastnosti primárního klíče (například proto, 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ím režimem pro ASP.NET Core a EF Core.

Webový server má 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 uvolní. 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í. Když proces čeká na dokončení vstupně-výstupních operací, uvolní se u asynchronního kódu, aby server používal ke zpracování jiný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 v případě nízkých přenosů je dosažení výkonu zanedbatelné, zatímco u vysokých dopravních situací je potenciální zlepšení výkonu podstatné.

V následujícím kódu async klíčové slovo, Task<T> návratová hodnota, await klíčové slovo a ToListAsync metoda vytvoří 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 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í).