Kurz: Začínáme s EF Core webovou aplikací ASP.NET MVC

Tom Dykstra a Rick Anderson

V tomto kurzu se učí 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 Pages přes MVC s kontrolery a zobrazeními. Podívejte se na verzi Razor Pages tohoto kurzu. Každý kurz se zabývá některými materiály, které ostatní kurzy neřeší:

Některé věci, které tento kurz MVC obsahuje, a kurz Razor Pages nikoli:

  • 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é kurz Razor Pages obsahuje, ale tento kurz ne:

  • Načtení souvisejících dat pomocí metody Select
  • Doporučené postupy pro EF

Ukázková webová aplikace Contoso Pro vysokoškoláky ukazuje, jak vytvořit webovou aplikaci ASP.NET Core MVC pomocí Entity Frameworku (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

Tento kurz nebyl aktualizován pro ASP.NET Core 6 nebo novější. Pokyny kurzu nebudou fungovat správně, pokud vytvoříte projekt, který cílí na ASP.NET Core 6 nebo novějším. Například webové šablony ASP.NET Core 6 a novější používají minimální model hostování, který se sjednocuje Startup.cs do Program.cs jednoho Program.cs souboru.

Dalším rozdílem zavedeným v .NET 6 je funkce NRT (odkazové typy s možnou hodnotou null). Šablony projektů tuto funkci ve výchozím nastavení povolují. K problémům může dojít v případě, že ef považuje vlastnost za požadovanou v .NET 6, která má hodnotu null v .NET 5. Například stránka Vytvořit studenta selže bezobslužně, pokud Enrollments není vlastnost nastavena nullable nebo asp-validation-summary pomocné značky se změní z ModelOnly na All.

Pro účely tohoto kurzu doporučujeme nainstalovat a používat sadu .NET 5 SDK. Dokud se tento kurz neaktualizuje, podívejte se na Razor stránky s Entity Framework Core v ASP.NET Core – kurz 1 z 8 o používání Entity Frameworku s ASP.NET Core 6 nebo novějším.

Databázové stroje

Pokyny pro 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 sérii. Pokud nenajdete, co tam potřebujete, můžete zadat dotaz na 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 každém úspěšném dokončení kurzu zvažte uložení kopie projektu. Pokud narazíte na problémy, můžete začít znovu z předchozího kurzu místo návratu na začátek celé série.

Webová aplikace Contoso University

Aplikace vytvořená 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 ContosoUniversity název projektu. Je důležité použít tento přesný název včetně velkých písmen, takže se každý z nich namespace shoduje 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 základní webová aplikace (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>

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 pro stránky vytvořené v tomto kurzu.

Contoso University home page

EF Core Balíčky 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 EF Core chybové stránky. Tento middleware pomáhá zjišťovat a diagnostikovat chyby při EF Core migracích.

Informace o jiných zprostředkovatelů databáze, které jsou k dispozici EF Core, naleznete v tématu Zprostředkovatelé 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 zapsán do libovolného 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 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íč. Například PK může být pojmenována StudentID spíše než 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 této 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. Když ICollection<T> se použije, EF ve výchozím nastavení vytvoří kolekci HashSet<T> .

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 používá vzor místo ID sebe sama. 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 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 má název <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 lze také pojmenovat název <>vlastnosti primárního klíče. Například proto, CourseID že Course pk entity je CourseID.

Entita Course

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 počtem Enrollment entit.

Atribut DatabaseGenerated je vysvětlen v pozdějším kurzu. Tento atribut umožňuje zadat PK pro kurz místo toho, aby ho 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 zahrnuty do 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 třídu s následujícím kódemSchoolContext:

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 .

Po 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 je výchozí chování přepsáno zadáním názvů singulárních 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");
        }
    }
}

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, jako jsou kontrolery MVC, jsou 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 ConfigureServices metody zvýrazněné řádky.

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í řetězec se předává kontextu voláním metody na objektuDbContextOptionsBuilder. V případě místního vývoje 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, 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 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. Ve výchozím nastavení vytvoří LocalDB 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 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 změnit schéma 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, neprochází žádná akce.

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 .
  • Odstraňte kontext po Initialize dokončení metody, 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 pozdější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 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í uživatelského rozhraní.

  • 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 tlačítko 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 datovém kontextu třídy vyberte SchoolContext.
    • Jako název přijměte výchozí StudentsController .
    • Klikněte na tlačítko 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. Nakonfigurovali jste ji ve Startup třídě.

Kontroler obsahuje metodu Index akce, která zobrazí 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 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 > 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 odkaz na kartu v horní části stránky 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

Po spuštění DbInitializer.Initialize aplikace volá metoda EnsureCreated. Ef viděl, že neexistuje žádná databáze:

  • Proto se vytvořila databáze.
  • Kód Initialize metody naplnil databázi daty.

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

  • V nabídce Zobrazit v sadě Visual Studio vyberte Průzkumník objektů SQL Serveru.
  • V nástroji SSOX vyberte databáze (localdb)\MSSQLLocalDB>.
  • Vyberte ContosoUniversity1položku pro název databáze, která je v připojovací řetězec v appsettings.json souboru.
  • Rozbalením uzlu Tabulky zobrazíte 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 je volána 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 této změně.

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

Konvence

Množství kódu napsaného za účelem 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 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, které jsou pojmenovány 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 lze také pojmenovat název <>vlastnosti primárního klíče. Například vzhledem k tomu, EnrollmentID že Enrollment pk entity je EnrollmentID.

Konvenční chování lze přepsat. Můžete například explicitně zadat názvy tabulek, 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. V takovém případě nemůže server zpracovat nové požadavky, dokud se vlákna nevysadí. V 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í, v asynchronním kódu se jeho vlákno 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í serverových prostředků a server je povolený pro zpracování většího provozu bez zpoždění.

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

V následujícím kódu , async, Task<T>awaitasynchronně ToListAsync provést kód.

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 aby 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á při dokončení operace.
  • ToListAsync je asynchronní verze ToList metody rozšíření.

Při psaní asynchronního kódu, který používá EF, je potřeba mít na paměti některé věci:

  • 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é jen mění , IQueryablenapří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í. Když voláte jakoukoli asynchronní metodu await EF, vždy použijte 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žívané také používají asynchronní, pokud volají jakékoli metody EF, které způsobují odesílání dotazů do databáze.

Další informace o asynchronním programování v .NET naleznete 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

Konfiguraci protokolování obvykle zajišťuje oddíl Logging souborů appsettings.{Environment}.json. 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": "*"
}

U předchozích JSpříkazů ON se příkazy SQL zobrazí na pří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 tento problém na GitHubu.

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

V tomto kurzu se učí 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 Pages přes MVC s kontrolery a zobrazeními. Podívejte se na verzi Razor Pages tohoto kurzu. Každý kurz se zabývá některými materiály, které ostatní kurzy neřeší:

Některé věci, které tento kurz MVC obsahuje, a kurz Razor Pages nikoli:

  • 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é kurz Razor Pages obsahuje, ale tento kurz ne:

  • Načtení souvisejících dat pomocí metody Select
  • Doporuč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 2019.

Tento kurz nebyl aktualizován pro ASP.NET Core 3.1. Aktualizoval se 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í z řady kurzů, které vysvětlují, jak vytvořit ukázkovou aplikaci Contoso University úplně od začátku.

Požadavky

Řešení problému

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 sérii. Pokud nenajdete, co tam potřebujete, můžete zadat dotaz na 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 každém úspěšném dokončení kurzu zvažte uložení kopie projektu. Pokud narazíte na problémy, můžete začít znovu 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 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ý > projekt.

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

  • Vyberte šablonu projektu ASP.NET Základní webová aplikace.

  • Jako název zadejte ContosoUniversity a klikněte na 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 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>

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 EF Core balíčcích NuGet

Pokud chcete do projektu přidat EF Core podporu, 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 pro EF. Balíček nástrojů přidáte později v kurzu Migrace .

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

Vytvoření datového modelu

V dalším kroku 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 existuje vztah StudentEnrollment 1:N a mezi entitami existuje vztah CourseEnrollment 1:N. Jinými slovy, student může být zapsán do libovolného počtu kurzů a kurz může mít v něm zaregistrovaný 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 sloupcem primárního klíče v tabulce databáze, 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 vlastnost objektu Student entity bude 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, které obsahují 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 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 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; }
    }
}

Vlastnost EnrollmentID bude primárním klíčem. Tato entita používá classnameID vzor místo ID sebe sama, 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 znázorněno, ž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 nullable. Známka, která má hodnotu null, se liší od nulové známky – hodnota null znamená, že známka není známá 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 tato vlastnost může obsahovat pouze 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 proto, CourseID že Course primární klíč entity je CourseID).

Entita Course

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 počtem Enrollment entit.

Další informace o atributu DatabaseGenerated si řekneme v pozdějším kurzu této série. Tento atribut v podstatě umožňuje zadat primární klíč pro kurz, nikoli databázi vygenerovat.

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 Framework. 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> příkazy a DbSet<Course> příkazy by fungovaly stejně. Entity Framework by je zahrnoval implicitně, protože entita Student odkazuje na entitu Enrollment a entitu Enrollment odkazuje na entitu Course .

Po vytvoření databáze ef vytvoří tabulky, které mají názvy stejné jako DbSet názvy vlastností. Názvy vlastností kolekcí jsou obvykle množné číslo (Studenti místo studenta), ale 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 přepíšete výchozí chování 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");
        }
    }
}

Sestavte projekt jako kontrolu chyb kompilátoru.

Registrace SchoolContextu

ASP.NET Core ve výchozím nastavení implementuje injektáž závislostí. Služby (například 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. 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 ConfigureServices metody zvýrazněné řádky.

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í řetězec se předává kontextu voláním metody na objektuDbContextOptionsBuilder. V případě místního vývoje 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 obory názvů a Microsoft.EntityFrameworkCore 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 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. Ve výchozím nastavení vytvoří LocalDB .mdf databázových souborů v C:/Users/<user> adresáři.

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 se naplnila testovacími daty.

Tady 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í 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 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 je databáze 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 a zobrazení akcí CRUD se označuje jako generování uživatelského rozhraní. 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 vygenerovaný kód přizpůsobit, 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 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 tlačítko Přidat. Zobrazí se dialogové okno Přidat kontroler MVC se zobrazením pomocí entity Framework : Scaffold Student
    • V možnosti Model třída vyberte Student.
    • V datovém kontextu třídy vyberte SchoolContext.
    • Jako název přijměte výchozí StudentsController .
    • Klikněte na tlačítko 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á zobrazí 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 > 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 odkaz na kartu v horní části stránky 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ěla, ž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 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 sadě Visual Studio.

V nástroji SSOX klikněte na databáze (localdb)\MSSQLLocalDB >a potom klikněte na položku názvu databáze, která je v připojovací řetězec v appsettings.json souboru.

Rozbalením uzlu Tabulky zobrazíte 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> .

Vzhledem k tomu, že voláte EnsureCreated metodu inicializátoru, která běží na spuštění aplikace, můžete nyní provést změnu Student třídy, odstranit databázi, spustit aplikaci znovu a databáze se automaticky znovu vytvoří tak, aby odpovídala vaší změně. Pokud například do třídy přidáte EmailAddress vlastnost Student , 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 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 je interpretována jako vlastnost cizího klíče, pokud se jmenuje název vlastnosti navigace název><vlastnosti> primárního klíče (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 <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 této série.

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. V takovém případě nemůže server zpracovat nové požadavky, dokud se vlákna nevysadí. V 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í, v asynchronním kódu se jeho vlákno 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í serverových prostředků a server je povolený pro zpracování většího provozu bez zpoždění.

Asynchronní kód zavádí v době běhu malé režijní náklady, ale v situacích s nízkým provozem je dosažení výkonu zanedbatelné, zatímco v situacích 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> návratová hodnota, await klíčové slovo a ToListAsync metoda, 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 aby 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á při dokončení operace.
  • ToListAsync je asynchronní verze ToList metody rozšíření.

Při psaní asynchronního kódu, který používá Entity Framework, je potřeba mít na paměti některé věci:

  • 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é jen mění , IQueryablenapří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í. Když voláte jakoukoli asynchronní metodu await EF, vždy použijte 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 naleznete 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í).