Razor Strony z programem Entity Framework Core w ASP.NET Core — samouczek 1 z 8

Przez Tom Dykstra, Jeremy Likness i Jon P Smith

Jest to pierwszy z serii samouczków, które pokazują, jak używać programu Entity Framework (EF) Core w aplikacji ASP.NET Core Razor Pages . Samouczki tworzą witrynę internetową fikcyjnego uniwersytetu Contoso. Witryna zawiera funkcje, takie jak wstęp dla uczniów, tworzenie kursów i zadania instruktora. W tym samouczku jest używane pierwsze podejście do kodu. Aby uzyskać informacje na temat korzystania z tego samouczka przy użyciu pierwszego podejścia do bazy danych, zobacz ten problem z usługą GitHub.

Pobierz lub wyświetl ukończoną aplikację.Pobierz instrukcje.

Wymagania wstępne

Aparaty bazy danych

Instrukcje programu Visual Studio używają programu SQL Server LocalDB — wersji programu SQL Server Express działającej tylko w systemie Windows.

Rozwiązywanie problemów

Jeśli napotkasz problem, nie możesz go rozwiązać, porównaj kod z ukończonym projektem. Dobrym sposobem uzyskania pomocy jest opublikowanie pytania do StackOverflow.com przy użyciu tagu ASP.NET Core lub taguEF Core.

Przykładowa aplikacja

Aplikacja wbudowana w te samouczki jest podstawową witryną internetową uniwersytetu. Użytkownicy mogą wyświetlać i aktualizować informacje o uczniach, kursach i instruktorach. Poniżej przedstawiono kilka ekranów utworzonych w samouczku.

Students Index page

Students Edit page

Styl interfejsu użytkownika tej witryny jest oparty na wbudowanych szablonach projektów. Samouczek koncentruje się na sposobie używania z EF Core platformą ASP.NET Core, a nie na dostosowywaniu interfejsu użytkownika.

Opcjonalnie: skompiluj przykładowy plik do pobrania

To krok jest opcjonalny. Kompilowanie ukończonej aplikacji jest zalecane, gdy masz problemy, których nie można rozwiązać. Jeśli napotkasz problem, nie możesz go rozwiązać, porównaj kod z ukończonym projektem. Pobierz instrukcje.

Wybierz, ContosoUniversity.csproj aby otworzyć projekt.

  • Skompiluj projekt.

  • W konsoli Menedżer pakietów (PMC) uruchom następujące polecenie:

    Update-Database
    

Uruchom projekt, aby zainicjować bazę danych.

Tworzenie projektu aplikacji internetowej

  1. Uruchom program Visual Studio 2022 i wybierz pozycję Utwórz nowy projekt.

    Create a new project from the start window

  2. W oknie dialogowym Tworzenie nowego projektu wybierz pozycję ASP.NET Core Web App, a następnie wybierz pozycję Dalej.

    Create an ASP.NET Core Web App

  3. W oknie dialogowym Konfigurowanie nowego projektu wprowadź wartość ContosoUniversity w polu Nazwa projektu. Ważne jest, aby nazwać projekt ContosoUniversity, w tym dopasowanie liter, więc przestrzenie nazw będą zgodne podczas kopiowania i wklejania przykładowego kodu.

  4. Wybierz pozycję Dalej.

  5. W oknie dialogowym Dodatkowe informacje wybierz pozycję .NET 6.0 (obsługa długoterminowa), a następnie wybierz pozycję Utwórz.

    Additional information

Konfigurowanie stylu witryny

Skopiuj i wklej następujący kod do Pages/Shared/_Layout.cshtml pliku:

<!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" asp-append-version="true" />
    <link rel="stylesheet" href="~/ContosoUniversity.styles.css" asp-append-version="true" />
</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-page="/Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-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-page="/About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Departments/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; 2021 - Contoso University - <a asp-area="" asp-page="/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>

Plik układu ustawia nagłówek, stopkę i menu witryny. Powyższy kod wprowadza następujące zmiany:

  • Każde wystąpienie elementu "ContosoUniversity" do "Contoso University". Istnieją trzy wystąpienia.
  • Wpisy Home menu i Privacy są usuwane.
  • Wpisy są dodawane dla pozycji Informacje, Studenci, Kursy, Instruktorzy i Działy.

W Pages/Index.cshtmlpliku zastąp zawartość pliku następującym kodem:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
@*                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
*@                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
@*                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
*@                </p>
            </div>
        </div>
    </div>
</div>

Powyższy kod zastępuje tekst ASP.NET Core tekstem dotyczącym tej aplikacji.

Uruchom aplikację, aby sprawdzić, czy zostanie wyświetlona strona główna.

Model danych

W poniższych sekcjach tworzysz model danych:

Course-Enrollment-Student data model diagram

Student może zarejestrować się w dowolnej liczbie kursów, a kurs może mieć dowolną liczbę uczniów zarejestrowanych w nim.

Jednostka Student

Student entity diagram

  • Utwórz folder Models w folderze projektu.
  • Utwórz Models/Student.cs za pomocą następującego kodu:
    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; }
        }
    }
    

Właściwość ID staje się kolumną klucza podstawowego tabeli bazy danych, która odpowiada tej klasie. Domyślnie EF Core interpretuje właściwość o nazwie ID lub classnameID jako klucz podstawowy. Dlatego alternatywna nazwa automatycznie rozpoznawana dla klucza podstawowego Student klasy to StudentID. Aby uzyskać więcej informacji, zobacz EF Core — Klucze.

Właściwość Enrollments jest właściwością nawigacji. Właściwości nawigacji przechowują inne jednostki powiązane z tą jednostką. W takim przypadku Enrollments właściwość Student jednostki przechowuje wszystkie Enrollment jednostki powiązane z tym uczniem. Jeśli na przykład wiersz Student w bazie danych zawiera dwa powiązane wiersze rejestracji, Enrollments właściwość nawigacji zawiera te dwie jednostki rejestracji.

W bazie danych wiersz rejestracji jest powiązany z wierszem Student, jeśli jego StudentID kolumna zawiera wartość identyfikatora ucznia. Załóżmy na przykład, że wiersz Student ma identyfikator =1. Powiązane wiersze rejestracji będą miały StudentID wartość = 1. StudentIDjest kluczem obcym w tabeli Rejestracja.

Właściwość jest zdefiniowana Enrollments jako ICollection<Enrollment> , ponieważ może istnieć wiele powiązanych jednostek rejestracji. Można użyć innych typów kolekcji, takich jak List<Enrollment> lub HashSet<Enrollment>. Gdy ICollection<Enrollment> jest używany, EF Core domyślnie tworzy HashSet<Enrollment> kolekcję.

Jednostka Rejestracja

Enrollment entity diagram

Utwórz Models/Enrollment.cs za pomocą następującego kodu:

using System.ComponentModel.DataAnnotations;

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; }
        [DisplayFormat(NullDisplayText = "No grade")]
        public Grade? Grade { get; set; }

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

Właściwość jest kluczem podstawowym. Ta EnrollmentID jednostka używa classnameID wzorca zamiast ID samego siebie. W przypadku modelu danych produkcyjnych wielu deweloperów wybiera jeden wzorzec i konsekwentnie go używa. W tym samouczku użyto obu tych elementów, aby zilustrować, że obie te elementy działają. Użycie ID bez classname ułatwia implementowanie niektórych rodzajów zmian modelu danych.

Właściwość Grade to enum. Znak zapytania po Grade deklaracji typu wskazuje, że Grade właściwość jest dopuszczana do wartości null. Ocena o wartości null różni się od klasy zerowej — wartość null oznacza, że ocena nie jest znana lub nie została jeszcze przypisana.

Właściwość StudentID jest kluczem obcym, a odpowiadająca mu właściwość nawigacji to Student. Jednostka Enrollment jest skojarzona z jedną Student jednostką, więc właściwość zawiera jedną Student jednostkę.

Właściwość CourseID jest kluczem obcym, a odpowiadająca mu właściwość nawigacji to Course. Jednostka Enrollment jest skojarzona z jedną jednostką Course .

EF Core interpretuje właściwość jako klucz obcy, jeśli ma nazwę <navigation property name><primary key property name>. Na przykładStudentID jest kluczem obcym Student właściwości nawigacji, ponieważ Student kluczem podstawowym jednostki jest ID. Właściwości klucza obcego mogą również mieć nazwę <primary key property name>. Na przykład ponieważ CourseIDCourse kluczem podstawowym jednostki jest CourseID.

Jednostka Course

Course entity diagram

Utwórz Models/Course.cs za pomocą następującego kodu:

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

Właściwość Enrollments jest właściwością nawigacji. Jednostka Course może być powiązana z dowolną Enrollment liczbą jednostek.

Atrybut DatabaseGenerated umożliwia aplikacji określenie klucza podstawowego zamiast generowania jej przez bazę danych.

Skompiluj aplikację. Kompilator generuje kilka ostrzeżeń dotyczących sposobu null obsługi wartości. Aby uzyskać więcej informacji, zobacz ten problem z usługą GitHub, typy referencyjne dopuszczane do wartości Null i Samouczek: Wyrażanie intencji projektowej przy użyciu typów odwołań dopuszczanych do wartości null i innych niż null.

Aby wyeliminować ostrzeżenia z typów odwołań dopuszczanych do wartości null, usuń następujący wiersz z ContosoUniversity.csproj pliku:

<Nullable>enable</Nullable>

Aparat tworzenia szkieletów obecnie nie obsługuje typów odwołań dopuszczających wartość null, dlatego modele używane w szkieletach nie mogą też być obsługiwane.

Usuń adnotację ? typu odwołania dopuszczanego z wartości null, Pages/Error.cshtml.cspublic string? RequestId { get; set; } aby kompilować projekt bez ostrzeżeń kompilatora.

Strony ucznia szkieletu

W tej sekcji narzędzie do tworzenia szkieletów ASP.NET Core służy do generowania:

  • Klasa EF CoreDbContext . Kontekst jest główną klasą, która koordynuje funkcje programu Entity Framework dla danego modelu danych. Pochodzi z Microsoft.EntityFrameworkCore.DbContext klasy .
  • Razor strony obsługujące operacje Create, Read, Update i Delete (CRUD) dla Student jednostki.
  • Utwórz folder Pages/Students.
  • W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Pages/Students i wybierz polecenie Dodaj>nowy element szkieletowy.
  • W oknie dialogowym Dodawanie nowego elementu szkieletu:
    • Na lewej karcie wybierz pozycję Zainstalowane > wspólne >Razor strony
    • Wybierz pozycję Strony przy użyciu programu Entity Framework (CRUD)>ADD.Razor
  • W oknie dialogowym Dodawanie Razor stron przy użyciu programu Entity Framework (CRUD):
    • Z listy rozwijanej Klasa modelu wybierz pozycję Student (ContosoUniversity.Models).
    • W wierszu Klasy kontekstu danych wybierz + znak (plus).
      • Zmień nazwę kontekstu danych tak, aby zakończyła SchoolContext się zamiast ContosoUniversityContext. Zaktualizowana nazwa kontekstu: ContosoUniversity.Data.SchoolContext
      • Wybierz pozycję Dodaj , aby zakończyć dodawanie klasy kontekstu danych.
      • Wybierz pozycję Dodaj , aby zakończyć okno dialogowe Dodawanie Razor stron .

Następujące pakiety są instalowane automatycznie:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Jeśli poprzedni krok zakończy się niepowodzeniem, skompiluj projekt i spróbuj ponownie wykonać krok szkieletu.

Proces tworzenia szkieletów:

  • Tworzy Razor strony w folderze Pages/Students :
    • Create.cshtml i Create.cshtml.cs
    • Delete.cshtml i Delete.cshtml.cs
    • Details.cshtml i Details.cshtml.cs
    • Edit.cshtml i Edit.cshtml.cs
    • Index.cshtml i Index.cshtml.cs
  • Tworzy Data/SchoolContext.cspolecenie .
  • Dodaje kontekst do wstrzykiwania zależności w pliku Program.cs.
  • Dodaje parametry połączenia bazy danych do elementu appsettings.json.

Parametry połączenia bazy danych

Narzędzie do tworzenia szkieletów generuje parametry połączenia w appsettings.json pliku.

Parametry połączenia określa sql Server LocalDB:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext-0e9;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

LocalDB to uproszczona wersja aparatu bazy danych SQL Server Express i jest przeznaczona do tworzenia aplikacji, a nie do użytku produkcyjnego. Domyślnie baza danych LocalDB tworzy pliki mdf w C:/Users/<user> katalogu.

Aktualizowanie klasy kontekstu bazy danych

Główną klasą, która koordynuje EF Core funkcjonalność danego modelu danych, jest klasa kontekstu bazy danych. Kontekst pochodzi z elementu Microsoft.EntityFrameworkCore.DbContext. Kontekst określa, które jednostki są uwzględnione w modelu danych. W tym projekcie klasa nosi nazwę SchoolContext.

Zaktualizuj Data/SchoolContext.cs za pomocą następującego kodu:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

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

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

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

Powyższy kod zmienia się z liczby pojedynczej na liczbę mnogą DbSet<Student> StudentDbSet<Student> Students. Aby kod Razor Pages był zgodny z nową DBSet nazwą, wprowadź globalną zmianę z: _context.Student. do: _context.Students.

Istnieje 8 wystąpień.

Ponieważ zestaw jednostek zawiera wiele jednostek, wielu deweloperów preferuje DBSet nazwy właściwości powinny być mnogią.

Wyróżniony kod:

  • Tworzy DbSet<TEntity> właściwość dla każdego zestawu jednostek. Terminologia EF Core :
    • Zestaw jednostek zwykle odpowiada tabeli bazy danych.
    • Jednostka odpowiada wierszowi w tabeli.
  • Wywołuje OnModelCreating. OnModelCreating:
    • Jest wywoływany, gdy SchoolContext został zainicjowany, ale przed zablokowaniem modelu i użytym do zainicjowania kontekstu.
    • Jest to wymagane, ponieważ w dalszej części samouczka Student jednostka będzie zawierać odwołania do innych jednostek.

Mamy nadzieję rozwiązać ten problem w przyszłej wersji.

Program.cs

ASP.NET Core jest kompilowany za pomocą wstrzykiwania zależności. Usługi, takie jak te SchoolContext , są rejestrowane za pomocą wstrzykiwania zależności podczas uruchamiania aplikacji. Składniki, które wymagają tych usług, takich jak Razor Pages, są udostępniane za pomocą parametrów konstruktora. Kod konstruktora, który pobiera wystąpienie kontekstu bazy danych, jest wyświetlany w dalszej części samouczka.

Narzędzie do tworzenia szkieletów automatycznie zarejestrowało klasę kontekstu za pomocą kontenera wstrzykiwania zależności.

Następujące wyróżnione wiersze zostały dodane przez szkielet:

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDbContext<SchoolContext>(options =>
  options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));

Nazwa parametry połączenia jest przekazywana do kontekstu przez wywołanie metody w DbContextOptions obiekcie. W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje parametry połączenia z appsettings.json pliku lub appsettings.Development.json .

Dodawanie filtru wyjątku bazy danych

Dodaj AddDatabaseDeveloperPageExceptionFilter element i UseMigrationsEndPoint , jak pokazano w poniższym kodzie:

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDbContext<SchoolContext>(options =>
  options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));

builder.Services.AddDatabaseDeveloperPageExceptionFilter();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseMigrationsEndPoint();
}

Dodaj pakiet NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.

W konsoli Menedżer pakietów wprowadź następujące polecenie, aby dodać pakiet NuGet:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore Pakiet NuGet udostępnia oprogramowanie pośredniczące ASP.NET Core dla stron błędów programu Entity Framework Core. To oprogramowanie pośredniczące pomaga wykrywać i diagnozować błędy migracji programu Entity Framework Core.

Zawiera AddDatabaseDeveloperPageExceptionFilter przydatne informacje o błędach w środowisku projektowym pod kątem błędów migracji ef.

Tworzenie bazy danych

Zaktualizuj Program.cs bazę danych, aby utworzyć ją, jeśli nie istnieje:

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDbContext<SchoolContext>(options =>
  options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));

builder.Services.AddDatabaseDeveloperPageExceptionFilter();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseMigrationsEndPoint();
}

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<SchoolContext>();
    context.Database.EnsureCreated();
    // DbInitializer.Initialize(context);
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Metoda EnsureCreated nie podejmuje żadnej akcji, jeśli istnieje baza danych kontekstu. Jeśli baza danych nie istnieje, tworzy bazę danych i schemat. EnsureCreated włącza następujący przepływ pracy do obsługi zmian modelu danych:

  • Usuń bazę danych. Wszystkie istniejące dane zostaną utracone.
  • Zmień model danych. Na przykład dodaj EmailAddress pole.
  • Uruchom aplikację.
  • EnsureCreated tworzy bazę danych przy użyciu nowego schematu.

Ten przepływ pracy działa na wczesnym etapie opracowywania, gdy schemat jest szybko ewoluujący, o ile dane nie muszą być zachowywane. Sytuacja jest inna, gdy należy zachować dane wprowadzone w bazie danych. W takim przypadku należy użyć migracji.

W dalszej części serii samouczków baza danych zostanie usunięta, która została utworzona przez EnsureCreated program i zostanie użyta migracja. Nie można zaktualizować bazy danych utworzonej przez EnsureCreated program przy użyciu migracji.

Testowanie aplikacji

  • Uruchom aplikację.
  • Wybierz link Uczniowie, a następnie pozycję Utwórz nowy.
  • Przetestuj linki Edytuj, Szczegóły i Usuń.

Inicjowanie bazy danych

Metoda EnsureCreated tworzy pustą bazę danych. Ta sekcja dodaje kod, który wypełnia bazę danych danymi testowymi.

Utwórz Data/DbInitializer.cs za pomocą następującego kodu:

using ContosoUniversity.Models;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            // 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("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            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}
            };

            context.Courses.AddRange(courses);
            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},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

Kod sprawdza, czy w bazie danych znajdują się uczniowie. Jeśli nie ma uczniów, dodaje dane testowe do bazy danych. Tworzy dane testowe w tablicach, a nie List<T> kolekcje w celu zoptymalizowania wydajności.

  • W Program.cspliku DbInitializer.Initialize usuń // z wiersza:
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<SchoolContext>();
    context.Database.EnsureCreated();
    DbInitializer.Initialize(context);
}
  • Zatrzymaj aplikację, jeśli jest uruchomiona, i uruchom następujące polecenie w konsoli Menedżer pakietów (PMC):

    Drop-Database -Confirm
    
    
  • Odpowiedz, Y aby usunąć bazę danych.

  • Uruchom ponownie aplikację.
  • Wybierz stronę Uczniowie, aby wyświetlić dane rozstawione.

Wyświetlanie bazy danych

  • Otwórz program SQL Server Eksplorator obiektów (SSOX) z menu Widok w programie Visual Studio.
  • W programie SSOX wybierz pozycję (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. Nazwa bazy danych jest generowana na podstawie podanej wcześniej nazwy kontekstu oraz kreski i identyfikatora GUID.
  • Rozwiń węzeł Tabele.
  • Kliknij prawym przyciskiem myszy tabelę Student i kliknij pozycję Wyświetl dane , aby wyświetlić utworzone kolumny i wiersze wstawione do tabeli.
  • Kliknij prawym przyciskiem myszy tabelę Student i kliknij pozycję Wyświetl kod , aby zobaczyć, jak Student model jest mapowy na Student schemat tabeli.

Asynchroniczne metody EF w aplikacjach internetowych platformy ASP.NET Core

Programowanie asynchroniczne jest trybem domyślnym dla ASP.NET Core i EF Core.

Serwer internetowy ma ograniczoną liczbę dostępnych wątków, a w sytuacjach dużego obciążenia wszystkie dostępne wątki mogą być używane. W takim przypadku serwer nie może przetworzyć nowych żądań, dopóki wątki nie zostaną zwolnione. W przypadku kodu synchronicznego wiele wątków może być powiązanych, gdy nie działają, ponieważ oczekują na zakończenie operacji we/wy. W przypadku kodu asynchronicznego, gdy proces oczekuje na ukończenie operacji we/wy, jego wątek zostanie zwolniony do użycia przez serwer do przetwarzania innych żądań. W rezultacie kod asynchroniczny umożliwia wydajniejsze wykorzystanie zasobów serwera, a serwer może obsługiwać więcej ruchu bez opóźnień.

Kod asynchroniczny wprowadza niewielką ilość obciążeń w czasie wykonywania. W przypadku sytuacji o niskim natężeniu ruchu wydajność jest niewielka, podczas gdy w przypadku dużych sytuacji drogowych potencjalna poprawa wydajności jest znacząca.

W poniższym kodzie asynchroniczne słowo kluczowe, wartość zwracana, Taskawait słowo kluczowe i ToListAsync metoda sprawiają, że kod jest wykonywany asynchronicznie.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • Słowo async kluczowe nakazuje kompilatorowi:
  • Typ zwracany reprezentuje bieżącą Task pracę.
  • Słowo await kluczowe powoduje, że kompilator dzieli metodę na dwie części. Pierwsza część kończy się operacją, która została uruchomiona asynchronicznie. Druga część jest umieszczana w metodzie wywołania zwrotnego wywoływanej po zakończeniu operacji.
  • ToListAsync to asynchroniczna wersja ToList metody rozszerzenia.

Niektóre kwestie, o których należy pamiętać podczas pisania kodu asynchronicznego, który używa EF Coremetody :

  • Tylko instrukcje, które powodują wysyłanie zapytań lub poleceń do bazy danych, są wykonywane asynchronicznie. ToListAsyncObejmuje to , , FirstOrDefaultAsyncSingleOrDefaultAsynci SaveChangesAsync. Nie zawiera instrukcji, które po prostu zmieniają element IQueryable, na przykład var students = context.Students.Where(s => s.LastName == "Davolio").
  • Kontekst EF Core nie jest bezpieczny wątkiem: nie próbuj wykonywać wielu operacji równolegle.
  • Aby skorzystać z zalet wydajności kodu asynchronicznego, sprawdź, czy pakiety bibliotek (takie jak stronicowanie) używają asynchronicznego, jeśli wywołają EF Core metody wysyłające zapytania do bazy danych.

Aby uzyskać więcej informacji na temat programowania asynchronicznego na platformie .NET, zobacz Async Overview and Asynchronous programming with async and await (Omówienie asynchroniczne i asynchroniczne programowanie asynchroniczne za pomocą asynchronicznego i oczekiwania).

Ostrzeżenie

Implementacja asynchronicznego elementu Microsoft.Data.SqlClient ma znane problemy (#593, #601 i inne). Jeśli występują nieoczekiwane problemy z wydajnością, spróbuj zamiast tego użyć polecenia synchronizacji, szczególnie w przypadku obsługi dużych wartości tekstowych lub binarnych.

Zagadnienia dotyczące wydajności

Ogólnie rzecz biorąc, strona internetowa nie powinna ładować dowolnej liczby wierszy. Zapytanie powinno używać stronicowania lub podejścia ograniczającego. Na przykład powyższe zapytanie może użyć Take polecenia , aby ograniczyć zwracane wiersze:

public async Task OnGetAsync()
{
    Student = await _context.Students.Take(10).ToListAsync();
}

Wyliczanie dużej tabeli w widoku może zwrócić częściowo skonstruowaną odpowiedź HTTP 200, jeśli wyjątek bazy danych występuje w części przez wyliczenie.

Stronicowanie zostało omówione w dalszej części samouczka.

Aby uzyskać więcej informacji, zobacz Zagadnienia dotyczące wydajności (EF).

Następne kroki

Używanie narzędzia SQLite do programowania, programu SQL Server dla środowiska produkcyjnego

Jest to pierwszy z serii samouczków, które pokazują, jak używać programu Entity Framework (EF) Core w aplikacji ASP.NET Core Razor Pages . Samouczki tworzą witrynę internetową fikcyjnego uniwersytetu Contoso. Witryna zawiera funkcje, takie jak wstęp dla uczniów, tworzenie kursów i zadania instruktora. W tym samouczku jest używane pierwsze podejście do kodu. Aby uzyskać informacje na temat korzystania z tego samouczka przy użyciu pierwszego podejścia do bazy danych, zobacz ten problem z usługą GitHub.

Pobierz lub wyświetl ukończoną aplikację.Pobierz instrukcje.

Wymagania wstępne

Aparaty bazy danych

Instrukcje programu Visual Studio używają programu SQL Server LocalDB — wersji programu SQL Server Express działającej tylko w systemie Windows.

Rozwiązywanie problemów

Jeśli napotkasz problem, nie możesz go rozwiązać, porównaj kod z ukończonym projektem. Dobrym sposobem uzyskania pomocy jest opublikowanie pytania do StackOverflow.com przy użyciu tagu ASP.NET Core lub taguEF Core.

Przykładowa aplikacja

Aplikacja wbudowana w te samouczki jest podstawową witryną internetową uniwersytetu. Użytkownicy mogą wyświetlać i aktualizować informacje o uczniach, kursach i instruktorach. Poniżej przedstawiono kilka ekranów utworzonych w samouczku.

Students Index page

Students Edit page

Styl interfejsu użytkownika tej witryny jest oparty na wbudowanych szablonach projektów. Samouczek koncentruje się na sposobie używania z EF Core platformą ASP.NET Core, a nie na dostosowywaniu interfejsu użytkownika.

Opcjonalnie: skompiluj przykładowy plik do pobrania

To krok jest opcjonalny. Kompilowanie ukończonej aplikacji jest zalecane, gdy masz problemy, których nie można rozwiązać. Jeśli napotkasz problem, nie możesz go rozwiązać, porównaj kod z ukończonym projektem. Pobierz instrukcje.

Wybierz, ContosoUniversity.csproj aby otworzyć projekt.

  • Skompiluj projekt.
  • W konsoli Menedżer pakietów (PMC) uruchom następujące polecenie:
Update-Database

Uruchom projekt, aby zainicjować bazę danych.

Tworzenie projektu aplikacji internetowej

  1. Uruchom program Visual Studio i wybierz pozycję Utwórz nowy projekt.
  2. W oknie dialogowym Tworzenie nowego projektu wybierz pozycję ASP.NET Core Web Application>Next (Dalej).
  3. W oknie dialogowym Konfigurowanie nowego projektu wprowadź wartość ContosoUniversity w polu Nazwa projektu. Należy użyć tej dokładnej nazwy, w tym wielkich liter, więc każdy namespace z nich jest zgodny podczas kopiowania kodu.
  4. Wybierz pozycję Utwórz.
  5. W oknie dialogowym Tworzenie nowej aplikacji internetowej platformy ASP.NET Core wybierz pozycję:
    1. Platforma .NET Core i ASP.NET Core 5.0 na listach rozwijanych.
    2. ASP.NET Core Web App.
    3. UtwórzNew ASP.NET Core Project dialog

Konfigurowanie stylu witryny

Skopiuj i wklej następujący kod do Pages/Shared/_Layout.cshtml pliku:

<!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-page="/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-page="/About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Departments/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; 2021 - Contoso University - <a asp-area="" asp-page="/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>

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

Plik układu ustawia nagłówek, stopkę i menu witryny. Powyższy kod wprowadza następujące zmiany:

  • Każde wystąpienie elementu "ContosoUniversity" do "Contoso University". Istnieją trzy wystąpienia.
  • Wpisy Home menu i Privacy są usuwane.
  • Wpisy są dodawane dla pozycji Informacje, Studenci, Kursy, Instruktorzy i Działy.

W Pages/Index.cshtmlpliku zastąp zawartość pliku następującym kodem:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
                </p>
            </div>
        </div>
    </div>
</div>

Powyższy kod zastępuje tekst ASP.NET Core tekstem dotyczącym tej aplikacji.

Uruchom aplikację, aby sprawdzić, czy zostanie wyświetlona strona główna.

Model danych

W poniższych sekcjach tworzysz model danych:

Course-Enrollment-Student data model diagram

Student może zarejestrować się w dowolnej liczbie kursów, a kurs może mieć dowolną liczbę uczniów zarejestrowanych w nim.

Jednostka Student

Student entity diagram

  • Utwórz folder Models w folderze projektu.

  • Utwórz Models/Student.cs za pomocą następującego kodu:

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

Właściwość ID staje się kolumną klucza podstawowego tabeli bazy danych, która odpowiada tej klasie. Domyślnie EF Core interpretuje właściwość o nazwie ID lub classnameID jako klucz podstawowy. Dlatego alternatywna nazwa automatycznie rozpoznawana dla klucza podstawowego Student klasy to StudentID. Aby uzyskać więcej informacji, zobacz EF Core — Klucze.

Właściwość Enrollments jest właściwością nawigacji. Właściwości nawigacji przechowują inne jednostki powiązane z tą jednostką. W takim przypadku Enrollments właściwość Student jednostki przechowuje wszystkie Enrollment jednostki powiązane z tym uczniem. Jeśli na przykład wiersz Student w bazie danych zawiera dwa powiązane wiersze rejestracji, Enrollments właściwość nawigacji zawiera te dwie jednostki rejestracji.

W bazie danych wiersz rejestracji jest powiązany z wierszem Student, jeśli jego StudentID kolumna zawiera wartość identyfikatora ucznia. Załóżmy na przykład, że wiersz Student ma identyfikator =1. Powiązane wiersze rejestracji będą miały StudentID wartość = 1. StudentIDjest kluczem obcym w tabeli Rejestracja.

Właściwość jest zdefiniowana Enrollments jako ICollection<Enrollment> , ponieważ może istnieć wiele powiązanych jednostek rejestracji. Można użyć innych typów kolekcji, takich jak List<Enrollment> lub HashSet<Enrollment>. Gdy ICollection<Enrollment> jest używany, EF Core domyślnie tworzy HashSet<Enrollment> kolekcję.

Jednostka Rejestracja

Enrollment entity diagram

Utwórz Models/Enrollment.cs za pomocą następującego kodu:

using System.ComponentModel.DataAnnotations;

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; }
        [DisplayFormat(NullDisplayText = "No grade")]
        public Grade? Grade { get; set; }

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

Właściwość jest kluczem podstawowym. Ta EnrollmentID jednostka używa classnameID wzorca zamiast ID samego siebie. W przypadku modelu danych produkcyjnych wielu deweloperów wybiera jeden wzorzec i konsekwentnie go używa. W tym samouczku użyto obu tych elementów, aby zilustrować, że obie te elementy działają. Użycie ID bez classname ułatwia implementowanie niektórych rodzajów zmian modelu danych.

Właściwość Grade to enum. Znak zapytania po Grade deklaracji typu wskazuje, że Grade właściwość jest dopuszczana do wartości null. Ocena o wartości null różni się od klasy zerowej — wartość null oznacza, że ocena nie jest znana lub nie została jeszcze przypisana.

Właściwość StudentID jest kluczem obcym, a odpowiadająca mu właściwość nawigacji to Student. Jednostka Enrollment jest skojarzona z jedną Student jednostką, więc właściwość zawiera jedną Student jednostkę.

Właściwość CourseID jest kluczem obcym, a odpowiadająca mu właściwość nawigacji to Course. Jednostka Enrollment jest skojarzona z jedną jednostką Course .

EF Core interpretuje właściwość jako klucz obcy, jeśli ma nazwę <navigation property name><primary key property name>. Na przykładStudentID jest kluczem obcym Student właściwości nawigacji, ponieważ Student kluczem podstawowym jednostki jest ID. Właściwości klucza obcego mogą również mieć nazwę <primary key property name>. Na przykład ponieważ CourseIDCourse kluczem podstawowym jednostki jest CourseID.

Jednostka Course

Course entity diagram

Utwórz Models/Course.cs za pomocą następującego kodu:

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

Właściwość Enrollments jest właściwością nawigacji. Jednostka Course może być powiązana z dowolną Enrollment liczbą jednostek.

Atrybut DatabaseGenerated umożliwia aplikacji określenie klucza podstawowego zamiast generowania jej przez bazę danych.

Skompiluj projekt, aby sprawdzić, czy nie ma żadnych błędów kompilatora.

Strony ucznia szkieletu

W tej sekcji narzędzie do tworzenia szkieletów ASP.NET Core służy do generowania:

  • Klasa EF CoreDbContext . Kontekst jest główną klasą, która koordynuje funkcje programu Entity Framework dla danego modelu danych. Pochodzi z Microsoft.EntityFrameworkCore.DbContext klasy .
  • Razor strony obsługujące operacje Create, Read, Update i Delete (CRUD) dla Student jednostki.
  • Utwórz folder Pages/Students.
  • W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Pages/Students i wybierz polecenie Dodaj>nowy element szkieletowy.
  • W oknie dialogowym Dodawanie nowego elementu szkieletu:
    • Na lewej karcie wybierz pozycję Zainstalowane > wspólne >Razor strony
    • Wybierz pozycję Strony przy użyciu programu Entity Framework (CRUD)>ADD.Razor
  • W oknie dialogowym Dodawanie Razor stron przy użyciu programu Entity Framework (CRUD):
    • Z listy rozwijanej Klasa modelu wybierz pozycję Student (ContosoUniversity.Models).
    • W wierszu Klasy kontekstu danych wybierz + znak (plus).
      • Zmień nazwę kontekstu danych tak, aby zakończyła SchoolContext się zamiast ContosoUniversityContext. Zaktualizowana nazwa kontekstu: ContosoUniversity.Data.SchoolContext
      • Wybierz pozycję Dodaj , aby zakończyć dodawanie klasy kontekstu danych.
      • Wybierz pozycję Dodaj , aby zakończyć okno dialogowe Dodawanie Razor stron .

Jeśli tworzenie szkieletu zakończy się niepowodzeniem z powodu błędu 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.', uruchom ponownie narzędzie szkieletu lub zobacz ten problem z usługą GitHub.

Następujące pakiety są instalowane automatycznie:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Jeśli poprzedni krok zakończy się niepowodzeniem, skompiluj projekt i spróbuj ponownie wykonać krok szkieletu.

Proces tworzenia szkieletów:

  • Tworzy Razor strony w folderze Pages/Students :
    • Create.cshtml i Create.cshtml.cs
    • Delete.cshtml i Delete.cshtml.cs
    • Details.cshtml i Details.cshtml.cs
    • Edit.cshtml i Edit.cshtml.cs
    • Index.cshtml i Index.cshtml.cs
  • Tworzy Data/SchoolContext.cspolecenie .
  • Dodaje kontekst do wstrzykiwania zależności w pliku Startup.cs.
  • Dodaje parametry połączenia bazy danych do elementu appsettings.json.

Parametry połączenia bazy danych

Narzędzie do tworzenia szkieletów generuje parametry połączenia w appsettings.json pliku.

Parametry połączenia określa sql Server LocalDB:

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

LocalDB to uproszczona wersja aparatu bazy danych SQL Server Express i jest przeznaczona do tworzenia aplikacji, a nie do użytku produkcyjnego. Domyślnie baza danych LocalDB tworzy pliki mdf w C:/Users/<user> katalogu.

Aktualizowanie klasy kontekstu bazy danych

Główną klasą, która koordynuje EF Core funkcjonalność danego modelu danych, jest klasa kontekstu bazy danych. Kontekst pochodzi z elementu Microsoft.EntityFrameworkCore.DbContext. Kontekst określa, które jednostki są uwzględnione w modelu danych. W tym projekcie klasa nosi nazwę SchoolContext.

Zaktualizuj Data/SchoolContext.cs za pomocą następującego kodu:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

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

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

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

Powyższy kod zmienia się z liczby pojedynczej na liczbę mnogą DbSet<Student> StudentDbSet<Student> Students. Aby kod Razor Pages był zgodny z nową DBSet nazwą, wprowadź globalną zmianę z: _context.Student. do: _context.Students.

Istnieje 8 wystąpień.

Ponieważ zestaw jednostek zawiera wiele jednostek, wielu deweloperów preferuje DBSet nazwy właściwości powinny być mnogią.

Wyróżniony kod:

  • Tworzy DbSet<TEntity> właściwość dla każdego zestawu jednostek. Terminologia EF Core :
    • Zestaw jednostek zwykle odpowiada tabeli bazy danych.
    • Jednostka odpowiada wierszowi w tabeli.
  • Wywołuje OnModelCreating. OnModelCreating:
    • Jest wywoływany, gdy SchoolContext został zainicjowany, ale przed zablokowaniem modelu i użytym do zainicjowania kontekstu.
    • Jest to wymagane, ponieważ w dalszej części samouczka Student jednostka będzie zawierać odwołania do innych jednostek.

Skompiluj projekt, aby sprawdzić, czy nie ma żadnych błędów kompilatora.

Startup.cs

ASP.NET Core jest kompilowany za pomocą wstrzykiwania zależności. Usługi, takie jak te SchoolContext , są rejestrowane za pomocą wstrzykiwania zależności podczas uruchamiania aplikacji. Składniki, które wymagają tych usług, takich jak Razor Pages, są udostępniane za pomocą parametrów konstruktora. Kod konstruktora, który pobiera wystąpienie kontekstu bazy danych, jest wyświetlany w dalszej części samouczka.

Narzędzie do tworzenia szkieletów automatycznie zarejestrowało klasę kontekstu za pomocą kontenera wstrzykiwania zależności.

Następujące wyróżnione wiersze zostały dodane przez szkielet:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

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

Nazwa parametry połączenia jest przekazywana do kontekstu przez wywołanie metody w DbContextOptions obiekcie. W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje parametry połączenia z appsettings.json pliku.

Dodawanie filtru wyjątku bazy danych

Dodaj AddDatabaseDeveloperPageExceptionFilter element i UseMigrationsEndPoint , jak pokazano w poniższym kodzie:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

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

    services.AddDatabaseDeveloperPageExceptionFilter();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Dodaj pakiet NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.

W konsoli Menedżer pakietów wprowadź następujące polecenie, aby dodać pakiet NuGet:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore Pakiet NuGet udostępnia oprogramowanie pośredniczące ASP.NET Core dla stron błędów programu Entity Framework Core. To oprogramowanie pośredniczące pomaga wykrywać i diagnozować błędy migracji programu Entity Framework Core.

Zawiera AddDatabaseDeveloperPageExceptionFilter przydatne informacje o błędach w środowisku projektowym pod kątem błędów migracji ef.

Tworzenie bazy danych

Zaktualizuj Program.cs bazę danych, aby utworzyć ją, jeśli nie istnieje:

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>();
                    context.Database.EnsureCreated();
                    // 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>();
                });
    }
}

Metoda EnsureCreated nie podejmuje żadnej akcji, jeśli istnieje baza danych kontekstu. Jeśli baza danych nie istnieje, tworzy bazę danych i schemat. EnsureCreated włącza następujący przepływ pracy do obsługi zmian modelu danych:

  • Usuń bazę danych. Wszystkie istniejące dane zostaną utracone.
  • Zmień model danych. Na przykład dodaj EmailAddress pole.
  • Uruchom aplikację.
  • EnsureCreated tworzy bazę danych przy użyciu nowego schematu.

Ten przepływ pracy działa na wczesnym etapie opracowywania, gdy schemat jest szybko ewoluujący, o ile dane nie muszą być zachowywane. Sytuacja jest inna, gdy należy zachować dane wprowadzone w bazie danych. W takim przypadku należy użyć migracji.

W dalszej części serii samouczków baza danych zostanie usunięta, która została utworzona przez EnsureCreated program i zostanie użyta migracja. Nie można zaktualizować bazy danych utworzonej przez EnsureCreated program przy użyciu migracji.

Testowanie aplikacji

  • Uruchom aplikację.
  • Wybierz link Uczniowie, a następnie pozycję Utwórz nowy.
  • Przetestuj linki Edytuj, Szczegóły i Usuń.

Inicjowanie bazy danych

Metoda EnsureCreated tworzy pustą bazę danych. Ta sekcja dodaje kod, który wypełnia bazę danych danymi testowymi.

Utwórz Data/DbInitializer.cs za pomocą następującego kodu:

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

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            // 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("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            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}
            };

            context.Courses.AddRange(courses);
            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},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

Kod sprawdza, czy w bazie danych znajdują się uczniowie. Jeśli nie ma uczniów, dodaje dane testowe do bazy danych. Tworzy dane testowe w tablicach, a nie List<T> kolekcje w celu zoptymalizowania wydajności.

  • W Program.cspliku DbInitializer.Initialize usuń // z wiersza:

      context.Database.EnsureCreated();
      DbInitializer.Initialize(context);
    
  • Zatrzymaj aplikację, jeśli jest uruchomiona, i uruchom następujące polecenie w konsoli Menedżer pakietów (PMC):

    Drop-Database -Confirm
    
    
  • Odpowiedz, Y aby usunąć bazę danych.

  • Uruchom ponownie aplikację.
  • Wybierz stronę Uczniowie, aby wyświetlić dane rozstawione.

Wyświetlanie bazy danych

  • Otwórz program SQL Server Eksplorator obiektów (SSOX) z menu Widok w programie Visual Studio.
  • W programie SSOX wybierz pozycję (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. Nazwa bazy danych jest generowana na podstawie podanej wcześniej nazwy kontekstu oraz kreski i identyfikatora GUID.
  • Rozwiń węzeł Tabele.
  • Kliknij prawym przyciskiem myszy tabelę Student i kliknij pozycję Wyświetl dane , aby wyświetlić utworzone kolumny i wiersze wstawione do tabeli.
  • Kliknij prawym przyciskiem myszy tabelę Student i kliknij pozycję Wyświetl kod , aby zobaczyć, jak Student model jest mapowy na Student schemat tabeli.

Kod asynchroniczny

Programowanie asynchroniczne jest trybem domyślnym dla ASP.NET Core i EF Core.

Serwer internetowy ma ograniczoną liczbę dostępnych wątków, a w sytuacjach dużego obciążenia wszystkie dostępne wątki mogą być używane. W takim przypadku serwer nie może przetworzyć nowych żądań, dopóki wątki nie zostaną zwolnione. W przypadku kodu synchronicznego wiele wątków może być powiązanych, gdy nie działają, ponieważ oczekują na zakończenie operacji we/wy. W przypadku kodu asynchronicznego, gdy proces oczekuje na ukończenie operacji we/wy, jego wątek zostanie zwolniony do użycia przez serwer do przetwarzania innych żądań. W rezultacie kod asynchroniczny umożliwia wydajniejsze wykorzystanie zasobów serwera, a serwer może obsługiwać więcej ruchu bez opóźnień.

Kod asynchroniczny wprowadza niewielką ilość obciążeń w czasie wykonywania. W przypadku sytuacji o niskim natężeniu ruchu wydajność jest niewielka, podczas gdy w przypadku dużych sytuacji drogowych potencjalna poprawa wydajności jest znacząca.

W poniższym kodzie asynchroniczne słowo kluczowe, wartość zwracana, Taskawait słowo kluczowe i ToListAsync metoda sprawiają, że kod jest wykonywany asynchronicznie.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • Słowo async kluczowe nakazuje kompilatorowi:
  • Typ zwracany reprezentuje bieżącą Task pracę.
  • Słowo await kluczowe powoduje, że kompilator dzieli metodę na dwie części. Pierwsza część kończy się operacją, która została uruchomiona asynchronicznie. Druga część jest umieszczana w metodzie wywołania zwrotnego wywoływanej po zakończeniu operacji.
  • ToListAsync to asynchroniczna wersja ToList metody rozszerzenia.

Niektóre kwestie, o których należy pamiętać podczas pisania kodu asynchronicznego, który używa EF Coremetody :

  • Tylko instrukcje, które powodują wysyłanie zapytań lub poleceń do bazy danych, są wykonywane asynchronicznie. ToListAsyncObejmuje to , , FirstOrDefaultAsyncSingleOrDefaultAsynci SaveChangesAsync. Nie zawiera instrukcji, które po prostu zmieniają element IQueryable, na przykład var students = context.Students.Where(s => s.LastName == "Davolio").
  • Kontekst EF Core nie jest bezpieczny wątkiem: nie próbuj wykonywać wielu operacji równolegle.
  • Aby skorzystać z zalet wydajności kodu asynchronicznego, sprawdź, czy pakiety bibliotek (takie jak stronicowanie) używają asynchronicznego, jeśli wywołają EF Core metody wysyłające zapytania do bazy danych.

Aby uzyskać więcej informacji na temat programowania asynchronicznego na platformie .NET, zobacz Async Overview and Asynchronous programming with async and await (Omówienie asynchroniczne i asynchroniczne programowanie asynchroniczne za pomocą asynchronicznego i oczekiwania).

Zagadnienia dotyczące wydajności

Ogólnie rzecz biorąc, strona internetowa nie powinna ładować dowolnej liczby wierszy. Zapytanie powinno używać stronicowania lub podejścia ograniczającego. Na przykład powyższe zapytanie może użyć Take polecenia , aby ograniczyć zwracane wiersze:

public async Task OnGetAsync()
{
    Student = await _context.Students.Take(10).ToListAsync();
}

Wyliczanie dużej tabeli w widoku może zwrócić częściowo skonstruowaną odpowiedź HTTP 200, jeśli wyjątek bazy danych występuje w części przez wyliczenie.

MaxModelBindingCollectionSize wartość domyślna to 1024. Następujące zestawy MaxModelBindingCollectionSizekodu:

public void ConfigureServices(IServiceCollection services)
{
    var myMaxModelBindingCollectionSize = Convert.ToInt32(
                Configuration["MyMaxModelBindingCollectionSize"] ?? "100");

    services.Configure<MvcOptions>(options =>
           options.MaxModelBindingCollectionSize = myMaxModelBindingCollectionSize);

    services.AddRazorPages();

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

    services.AddDatabaseDeveloperPageExceptionFilter();
}

Zobacz Konfiguracja , aby uzyskać informacje na temat ustawień konfiguracji, takich jak MyMaxModelBindingCollectionSize.

Stronicowanie zostało omówione w dalszej części samouczka.

Aby uzyskać więcej informacji, zobacz Zagadnienia dotyczące wydajności (EF).

Rejestrowanie SQL platformy Entity Framework Core

Konfiguracja rejestrowania jest często dostarczana za pomocą sekcji Logging plików appsettings.{Environment}.json. Aby zarejestrować instrukcje SQL, dodaj "Microsoft.EntityFrameworkCore.Database.Command": "Information" do appsettings.Development.json pliku:

{
  "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": "*"
}

Po wcześniejszym wł. JSinstrukcje SQL są wyświetlane w wierszu polecenia i w oknie danych wyjściowych programu Visual Studio.

Aby uzyskać więcej informacji, zobacz Rejestrowanie na platformie .NET Core i ASP.NET Core oraz ten problem z usługą GitHub.

Następne kroki

Używanie narzędzia SQLite do programowania, programu SQL Server dla środowiska produkcyjnego

Jest to pierwszy z serii samouczków, które pokazują, jak używać programu Entity Framework (EF) Core w aplikacji ASP.NET Core Razor Pages . Samouczki tworzą witrynę internetową fikcyjnego uniwersytetu Contoso. Witryna zawiera funkcje, takie jak wstęp dla uczniów, tworzenie kursów i zadania instruktora. W tym samouczku jest używane pierwsze podejście do kodu. Aby uzyskać informacje na temat korzystania z tego samouczka przy użyciu pierwszego podejścia do bazy danych, zobacz ten problem z usługą GitHub.

Pobierz lub wyświetl ukończoną aplikację.Pobierz instrukcje.

Wymagania wstępne

Aparaty bazy danych

Instrukcje programu Visual Studio używają programu SQL Server LocalDB — wersji programu SQL Server Express działającej tylko w systemie Windows.

Instrukcje programu Visual Studio Code korzystają z narzędzia SQLite, aparatu bazy danych dla wielu platform.

Jeśli zdecydujesz się używać sqLite, pobierz i zainstaluj narzędzie innej firmy do zarządzania bazą danych SQLite i wyświetlania jej, takiej jak przeglądarka db for SQLite.

Rozwiązywanie problemów

Jeśli napotkasz problem, nie możesz go rozwiązać, porównaj kod z ukończonym projektem. Dobrym sposobem uzyskania pomocy jest opublikowanie pytania do StackOverflow.com przy użyciu tagu ASP.NET Core lub taguEF Core.

Przykładowa aplikacja

Aplikacja wbudowana w te samouczki jest podstawową witryną internetową uniwersytetu. Użytkownicy mogą wyświetlać i aktualizować informacje o uczniach, kursach i instruktorach. Poniżej przedstawiono kilka ekranów utworzonych w samouczku.

Students Index page

Students Edit page

Styl interfejsu użytkownika tej witryny jest oparty na wbudowanych szablonach projektów. Samouczek koncentruje się na sposobie używania metody EF Core, a nie dostosowywania interfejsu użytkownika.

Kliknij link w górnej części strony, aby pobrać kod źródłowy ukończonego projektu. Folder cu30 zawiera kod ASP.NET Core 3.0 w samouczku. Pliki odzwierciedlające stan kodu dla samouczków 1–7 można znaleźć w folderze cu30snapshots .

Aby uruchomić aplikację po pobraniu ukończonego projektu:

  • Skompiluj projekt.

  • W konsoli Menedżer pakietów (PMC) uruchom następujące polecenie:

    Update-Database
    
  • Uruchom projekt, aby zainicjować bazę danych.

Tworzenie projektu aplikacji internetowej

  • W menu Plik programu Visual Studio wybierz pozycję Nowy>projekt.
  • Wybierz pozycję ASP.NET Core Web Application(Podstawowa aplikacja internetowa).
  • Nadaj projektowi nazwę ContosoUniversity. Należy użyć tej dokładnej nazwy, w tym wielkich liter, więc przestrzenie nazw są zgodne, gdy kod jest kopiowany i wklejany.
  • Wybierz pozycję .NET Core i ASP.NET Core 3.0 na listach rozwijanych, a następnie wybierz pozycję Aplikacja internetowa.

Konfigurowanie stylu witryny

Skonfiguruj nagłówek, stopkę i menu witryny, aktualizując Pages/Shared/_Layout.cshtmlpolecenie :

  • Zmień każde wystąpienie "ContosoUniversity" na "Contoso University". Istnieją trzy wystąpienia.

  • Home Usuń wpisy menu i Privacy i dodaj wpisy dla pozycji Informacje, Uczniowie, Kursy, Instruktorzy i Działy.

Zmiany są wyróżnione.

<!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-page="/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-page="/About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Departments/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; 2019 - Contoso University - <a asp-area="" asp-page="/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>

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

W Pages/Index.cshtmlpliku zastąp zawartość pliku następującym kodem, aby zastąpić tekst ASP.NET Core tekstem o tej aplikacji:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
                </p>
            </div>
        </div>
    </div>
</div>

Uruchom aplikację, aby sprawdzić, czy zostanie wyświetlona strona główna.

Model danych

W poniższych sekcjach tworzysz model danych:

Course-Enrollment-Student data model diagram

Student może zarejestrować się w dowolnej liczbie kursów, a kurs może mieć dowolną liczbę uczniów zarejestrowanych w nim.

Jednostka Student

Student entity diagram

  • Utwórz folder Models w folderze projektu.

  • Utwórz Models/Student.cs za pomocą następującego kodu:

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

Właściwość ID staje się kolumną klucza podstawowego tabeli bazy danych, która odpowiada tej klasie. Domyślnie EF Core interpretuje właściwość o nazwie ID lub classnameID jako klucz podstawowy. Dlatego alternatywna nazwa automatycznie rozpoznawana dla klucza podstawowego Student klasy to StudentID. Aby uzyskać więcej informacji, zobacz EF Core — Klucze.

Właściwość Enrollments jest właściwością nawigacji. Właściwości nawigacji przechowują inne jednostki powiązane z tą jednostką. W takim przypadku Enrollments właściwość Student jednostki przechowuje wszystkie Enrollment jednostki powiązane z tym uczniem. Jeśli na przykład wiersz Student w bazie danych zawiera dwa powiązane wiersze rejestracji, Enrollments właściwość nawigacji zawiera te dwie jednostki rejestracji.

W bazie danych wiersz rejestracji jest powiązany z wierszem Student, jeśli jego kolumna StudentID zawiera wartość identyfikatora ucznia. Załóżmy na przykład, że wiersz Student ma identyfikator =1. Powiązane wiersze rejestracji będą miały identyfikator StudentID = 1. StudentID jest kluczem obcym w tabeli Rejestracja.

Właściwość jest zdefiniowana Enrollments jako ICollection<Enrollment> , ponieważ może istnieć wiele powiązanych jednostek rejestracji. Możesz użyć innych typów kolekcji, takich jak List<Enrollment> lub HashSet<Enrollment>. Gdy ICollection<Enrollment> jest używany, EF Core domyślnie tworzy HashSet<Enrollment> kolekcję.

Jednostka Rejestracja

Enrollment entity diagram

Utwórz Models/Enrollment.cs za pomocą następującego kodu:

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

Właściwość jest kluczem podstawowym. Ta EnrollmentID jednostka używa classnameID wzorca zamiast ID samego siebie. W przypadku modelu danych produkcyjnych wybierz jeden wzorzec i użyj go spójnie. W tym samouczku użyto obu tych elementów, aby zilustrować, że obie te elementy działają. Użycie ID bez classname ułatwia implementowanie niektórych rodzajów zmian modelu danych.

Właściwość Grade to enum. Znak zapytania po Grade deklaracji typu wskazuje, że Grade właściwość jest dopuszczana do wartości null. Ocena o wartości null różni się od klasy zerowej — wartość null oznacza, że ocena nie jest znana lub nie została jeszcze przypisana.

Właściwość StudentID jest kluczem obcym, a odpowiadająca mu właściwość nawigacji to Student. Jednostka Enrollment jest skojarzona z jedną Student jednostką, więc właściwość zawiera jedną Student jednostkę.

Właściwość CourseID jest kluczem obcym, a odpowiadająca mu właściwość nawigacji to Course. Jednostka Enrollment jest skojarzona z jedną jednostką Course .

EF Core interpretuje właściwość jako klucz obcy, jeśli ma nazwę <navigation property name><primary key property name>. Na przykładStudentID jest kluczem obcym Student właściwości nawigacji, ponieważ Student kluczem podstawowym jednostki jest ID. Właściwości klucza obcego mogą również mieć nazwę <primary key property name>. Na przykład ponieważ CourseIDCourse kluczem podstawowym jednostki jest CourseID.

Jednostka Course

Course entity diagram

Utwórz Models/Course.cs za pomocą następującego kodu:

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

Właściwość Enrollments jest właściwością nawigacji. Jednostka Course może być powiązana z dowolną Enrollment liczbą jednostek.

Atrybut DatabaseGenerated umożliwia aplikacji określenie klucza podstawowego zamiast generowania jej przez bazę danych.

Skompiluj projekt, aby sprawdzić, czy nie ma żadnych błędów kompilatora.

Strony ucznia szkieletu

W tej sekcji użyjesz narzędzia do tworzenia szkieletów ASP.NET Core, aby wygenerować następujące elementy:

  • Klasa EF Corekontekstu . Kontekst jest główną klasą, która koordynuje funkcje programu Entity Framework dla danego modelu danych. Pochodzi z Microsoft.EntityFrameworkCore.DbContext klasy .
  • Razor strony obsługujące operacje Create, Read, Update i Delete (CRUD) dla Student jednostki.
  • Utwórz folder Students w folderze Pages.
  • W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Pages/Students i wybierz polecenie Dodaj>nowy element szkieletowy.
  • W oknie dialogowym Dodawanie szkieletu wybierz pozycję Razor Strony przy użyciu programu Entity Framework (CRUD)>ADD.
  • W oknie dialogowym Dodawanie Razor stron przy użyciu programu Entity Framework (CRUD):
    • Z listy rozwijanej Klasa modelu wybierz pozycję Student (ContosoUniversity.Models).
    • W wierszu Klasy kontekstu danych wybierz + znak (plus).
    • Zmień nazwę kontekstu danych z ContosoUniversity.Models.ContosoUniversityContext na ContosoUniversity.Data.SchoolContext.
    • Wybierz pozycję Dodaj.

Następujące pakiety są instalowane automatycznie:

  • Microsoft.VisualStudio.Web.CodeGeneration.Design
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.Extensions.Logging.Debug
  • Microsoft.EntityFrameworkCore.Tools

Jeśli masz problem z poprzednim krokiem, skompiluj projekt i spróbuj ponownie wykonać krok szkieletu.

Proces tworzenia szkieletów:

  • Tworzy Razor strony w folderze Pages/Students :
    • Create.cshtml i Create.cshtml.cs
    • Delete.cshtml i Delete.cshtml.cs
    • Details.cshtml i Details.cshtml.cs
    • Edit.cshtml i Edit.cshtml.cs
    • Index.cshtml i Index.cshtml.cs
  • Tworzy Data/SchoolContext.cspolecenie .
  • Dodaje kontekst do wstrzykiwania zależności w pliku Startup.cs.
  • Dodaje parametry połączenia bazy danych do elementu appsettings.json.

Parametry połączenia bazy danych

Plik appsettings.json określa parametry połączenia SQL Server LocalDB.

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

LocalDB to uproszczona wersja aparatu bazy danych SQL Server Express i jest przeznaczona do tworzenia aplikacji, a nie do użytku produkcyjnego. Domyślnie baza danych LocalDB tworzy pliki mdf w C:/Users/<user> katalogu.

Aktualizowanie klasy kontekstu bazy danych

Główną klasą, która koordynuje EF Core funkcjonalność danego modelu danych, jest klasa kontekstu bazy danych. Kontekst pochodzi z elementu Microsoft.EntityFrameworkCore.DbContext. Kontekst określa, które jednostki są uwzględnione w modelu danych. W tym projekcie klasa nosi nazwę SchoolContext.

Zaktualizuj Data/SchoolContext.cs za pomocą następującego kodu:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

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

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

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

Wyróżniony DbSet<TEntity> kod tworzy właściwość dla każdego zestawu jednostek. Terminologia EF Core :

  • Zestaw jednostek zwykle odpowiada tabeli bazy danych.
  • Jednostka odpowiada wierszowi w tabeli.

Ponieważ zestaw jednostek zawiera wiele jednostek, właściwości DBSet powinny być nazwami mnogimi. Ponieważ narzędzie tworzenia szkieletu utworzyło zestawStudent DBSet, ten krok zmienia go na liczbę mnogą Students.

Aby kod Razor Pages był zgodny z nową nazwą zestawu DBSet, wprowadź globalną zmianę w całym projekcie _context.Student na _context.Students. Istnieje 8 wystąpień.

Skompiluj projekt, aby sprawdzić, czy nie ma żadnych błędów kompilatora.

Startup.cs

ASP.NET Core jest kompilowany za pomocą wstrzykiwania zależności. Usługi (takie jak EF Core kontekst bazy danych) są rejestrowane za pomocą wstrzykiwania zależności podczas uruchamiania aplikacji. Składniki, które wymagają tych usług (takich jak Razor Pages), są udostępniane za pośrednictwem parametrów konstruktora. Kod konstruktora, który pobiera wystąpienie kontekstu bazy danych, jest wyświetlany w dalszej części samouczka.

Narzędzie do tworzenia szkieletów automatycznie zarejestrowało klasę kontekstu za pomocą kontenera wstrzykiwania zależności.

  • W ConfigureServicespliku wyróżnione wiersze zostały dodane przez szkielet:

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

Nazwa parametry połączenia jest przekazywana do kontekstu przez wywołanie metody w DbContextOptions obiekcie. W przypadku programowania lokalnego system konfiguracji ASP.NET Core odczytuje parametry połączenia z appsettings.json pliku.

Tworzenie bazy danych

Zaktualizuj Program.cs bazę danych, aby utworzyć ją, jeśli nie istnieje:

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>();
                    context.Database.EnsureCreated();
                    // 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>();
                });
    }
}

Metoda EnsureCreated nie podejmuje żadnej akcji, jeśli istnieje baza danych kontekstu. Jeśli baza danych nie istnieje, tworzy bazę danych i schemat. EnsureCreated włącza następujący przepływ pracy do obsługi zmian modelu danych:

  • Usuń bazę danych. Wszystkie istniejące dane zostaną utracone.
  • Zmień model danych. Na przykład dodaj EmailAddress pole.
  • Uruchom aplikację.
  • EnsureCreated tworzy bazę danych przy użyciu nowego schematu.

Ten przepływ pracy działa dobrze na wczesnym etapie opracowywania, gdy schemat jest szybko rozwijany, o ile nie trzeba zachowywać danych. Sytuacja jest inna, gdy należy zachować dane wprowadzone w bazie danych. W takim przypadku należy użyć migracji.

W dalszej części serii samouczków usuniesz bazę danych, która została utworzona przez EnsureCreated program i zamiast tego użyjesz migracji. Nie można zaktualizować bazy danych utworzonej przez EnsureCreated program przy użyciu migracji.

Testowanie aplikacji

  • Uruchom aplikację.
  • Wybierz link Uczniowie, a następnie pozycję Utwórz nowy.
  • Przetestuj linki Edytuj, Szczegóły i Usuń.

Inicjowanie bazy danych

Metoda EnsureCreated tworzy pustą bazę danych. Ta sekcja dodaje kod, który wypełnia bazę danych danymi testowymi.

Utwórz Data/DbInitializer.cs za pomocą następującego kodu:

using ContosoUniversity.Data;
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("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            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}
            };

            context.Courses.AddRange(courses);
            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},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

Kod sprawdza, czy w bazie danych znajdują się uczniowie. Jeśli nie ma uczniów, dodaje dane testowe do bazy danych. Tworzy dane testowe w tablicach, a nie List<T> kolekcje w celu zoptymalizowania wydajności.

  • W Program.cspliku zastąp wywołanie EnsureCreated wywołaniem DbInitializer.Initialize :

    // context.Database.EnsureCreated();
    DbInitializer.Initialize(context);
    

Zatrzymaj aplikację, jeśli jest uruchomiona, i uruchom następujące polecenie w konsoli Menedżer pakietów (PMC):

Drop-Database
  • Uruchom ponownie aplikację.

  • Wybierz stronę Uczniowie, aby wyświetlić dane rozstawione.

Wyświetlanie bazy danych

  • Otwórz program SQL Server Eksplorator obiektów (SSOX) z menu Widok w programie Visual Studio.
  • W programie SSOX wybierz pozycję (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. Nazwa bazy danych jest generowana na podstawie podanej wcześniej nazwy kontekstu oraz kreski i identyfikatora GUID.
  • Rozwiń węzeł Tabele.
  • Kliknij prawym przyciskiem myszy tabelę Student i kliknij pozycję Wyświetl dane , aby wyświetlić utworzone kolumny i wiersze wstawione do tabeli.
  • Kliknij prawym przyciskiem myszy tabelę Student i kliknij pozycję Wyświetl kod , aby zobaczyć, jak Student model jest mapowy na Student schemat tabeli.

Kod asynchroniczny

Programowanie asynchroniczne jest trybem domyślnym dla ASP.NET Core i EF Core.

Serwer internetowy ma ograniczoną liczbę dostępnych wątków, a w sytuacjach dużego obciążenia wszystkie dostępne wątki mogą być używane. W takim przypadku serwer nie może przetworzyć nowych żądań, dopóki wątki nie zostaną zwolnione. W przypadku kodu synchronicznego wiele wątków może być powiązanych, gdy nie wykonują żadnej pracy, ponieważ oczekują na zakończenie operacji we/wy. W przypadku kodu asynchronicznego, gdy proces oczekuje na ukończenie operacji we/wy, jego wątek zostanie zwolniony do użycia przez serwer do przetwarzania innych żądań. W rezultacie kod asynchroniczny umożliwia wydajniejsze wykorzystanie zasobów serwera, a serwer może obsługiwać więcej ruchu bez opóźnień.

Kod asynchroniczny wprowadza niewielką ilość obciążeń w czasie wykonywania. W przypadku sytuacji o niskim natężeniu ruchu wydajność jest niewielka, podczas gdy w przypadku dużych sytuacji drogowych potencjalna poprawa wydajności jest znacząca.

W poniższym kodzie asynchroniczne słowo kluczowe, wartość zwracana, Task<T>await słowo kluczowe i ToListAsync metoda sprawiają, że kod jest wykonywany asynchronicznie.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • Słowo async kluczowe nakazuje kompilatorowi:
  • Typ zwracany reprezentuje bieżącą Task<T> pracę.
  • Słowo await kluczowe powoduje, że kompilator dzieli metodę na dwie części. Pierwsza część kończy się operacją, która została uruchomiona asynchronicznie. Druga część jest umieszczana w metodzie wywołania zwrotnego wywoływanej po zakończeniu operacji.
  • ToListAsync to asynchroniczna wersja ToList metody rozszerzenia.

Niektóre kwestie, o których należy pamiętać podczas pisania kodu asynchronicznego, który używa EF Coremetody :

  • Tylko instrukcje, które powodują wysyłanie zapytań lub poleceń do bazy danych, są wykonywane asynchronicznie. ToListAsyncObejmuje to , , FirstOrDefaultAsyncSingleOrDefaultAsynci SaveChangesAsync. Nie zawiera instrukcji, które po prostu zmieniają element IQueryable, na przykład var students = context.Students.Where(s => s.LastName == "Davolio").
  • Kontekst EF Core nie jest bezpieczny wątkiem: nie próbuj wykonywać wielu operacji równolegle.
  • Aby skorzystać z zalet wydajności kodu asynchronicznego, sprawdź, czy pakiety bibliotek (takie jak stronicowanie) używają asynchronicznego, jeśli wywołają EF Core metody wysyłające zapytania do bazy danych.

Aby uzyskać więcej informacji na temat programowania asynchronicznego na platformie .NET, zobacz Async Overview and Asynchronous programming with async and await (Omówienie asynchroniczne i asynchroniczne programowanie asynchroniczne za pomocą asynchronicznego i oczekiwania).

Następne kroki