ASP.NET Core에서 Entity Framework Core를 사용한 Razor Pages - 자습서 1/8

작성자 : Tom Dykstra, Jeremy Likness, Jon P Smith

ASP.NET Core Razor Pages 앱에서 EF(Entity Framework) Core를 사용하는 방법을 보여 주는 자습서 시리즈 중 첫 번째 자습서입니다. 이 자습서에서는 가상 Contoso 대학의 웹 사이트를 빌드합니다. 이 사이트에는 학생 입학, 강좌 개설 및 강사 할당과 같은 기능이 있습니다. 이 자습서에서는 Code First 방법을 사용합니다. Database First 방법을 사용하여 이 자습서를 수행하는 데 대한 정보는 이 Github 문제를 참조하세요.

완료된 앱을 다운로드하거나 봅니다.지침을 다운로드합니다.

필수 조건

  • Razor Pages를 처음 사용한다면 이 자습서를 시작하기 전에 Razor Razor Pages 시작 자습서 시리즈를 진행하세요.

데이터베이스 엔진

Visual Studio 지침에서는 Windows에서만 실행되는 SQL Server Express 버전인 SQL Server LocalDB를 사용합니다.

문제 해결

해결할 수 없는 문제가 발생한 경우 완료된 프로젝트와 코드를 비교합니다. 도움을 얻으려면 ASP.NET Core 태그 또는 EF Core태그를 사용하여 StackOverflow.com에 질문을 게시하는 것이 좋습니다.

샘플 앱

이러한 자습서에서 빌드한 앱은 기본 대학 웹 사이트입니다. 사용자는 학생, 강좌 및 강사 정보를 보고 업데이트할 수 있습니다. 자습서에서 만든 화면 중 몇 가지 예가 나와 있습니다.

Students Index page

Students Edit page

이 사이트의 UI 스타일은 기본 제공 프로젝트 템플릿을 기반으로 합니다. 이 자습서에서는 UI를 사용자 지정하는 방법이 아닌 EF Core를 ASP.NET Core와 함께 사용하는 방법을 중점적으로 설명합니다.

선택 사항: 샘플 다운로드 빌드

이 단계는 옵션입니다. 해결할 수 없는 문제가 발생하는 경우 완성된 앱을 빌드하는 것이 좋습니다. 해결할 수 없는 문제가 발생한 경우 완료된 프로젝트와 코드를 비교합니다. 지침을 다운로드하세요.

ContosoUniversity.csproj을 선택하여 프로젝트를 엽니다.

  • 프로젝트를 빌드합니다.

  • PMC(패키지 관리자 콘솔)에서 다음 명령을 실행합니다.

    Update-Database
    

프로젝트를 실행하여 데이터베이스를 시드합니다.

웹앱 프로젝트 만들기

  1. Visual Studio 2022를 시작하고 새 프로젝트 만들기를 선택합니다.

    Create a new project from the start window

  2. 새 프로젝트 만들기 대화 상자에서 ASP.NET Core 웹앱을 선택한 후, 다음을 선택합니다.

    Create an ASP.NET Core Web App

  3. 새 프로젝트 구성 대화 상자에서 프로젝트 이름으로 ContosoUniversity를 입력합니다. 대문자 표시를 포함하여 프로젝트 이름을 ContosoUniversity로 지정해야 예제 코드를 복사해 붙여넣을 때 네임스페이스가 일치합니다.

  4. 다음을 선택합니다.

  5. 추가 정보 대화 상자에서 .NET 6.0(장기 지원)을 선택한 다음 만들기를 선택합니다.

    Additional information

사이트 스타일 설정

다음 코드를 복사하여 Pages/Shared/_Layout.cshtml 파일에 붙여넣습니다.

<!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>

레이아웃 파일은 사이트 머리글, 바닥글 및 메뉴를 설정합니다. 위의 코드로 다음이 변경됩니다.

  • 모든 “ContosoUniversity”가 “Contoso University”로 변경됩니다. 세 번 나옵니다.
  • HomePrivacy 메뉴 항목이 삭제됩니다.
  • 정보,학생, 과정, 강사부서에 대해 항목이 추가됩니다.

Pages/Index.cshtml에서 파일의 콘텐츠를 다음 코드로 바꿉니다.

@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>

위의 코드는 ASP.NET Core에 대한 텍스트를 이 앱에 대한 텍스트로 바꿉니다.

앱을 실행하여 홈페이지가 표시되는지 확인합니다.

데이터 모델

다음 섹션에서는 데이터 모델을 만듭니다.

Course-Enrollment-Student data model diagram

학생은 개수와 관계없이 원하는 과정에 등록할 수 있으며 한 과정에는 여러 명이 등록될 수 있습니다.

학생 엔터티

Student entity diagram

  • 프로젝트 폴더에 Models 폴더를 만듭니다.
  • 다음 코드를 사용하여 만듭니 Models/Student.cs 다.
    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; }
        }
    }
    

ID 속성은 이 클래스에 해당하는 데이터베이스 테이블의 기본 키 열이 됩니다. 기본적으로 EF Core는 이름이 ID 또는 classnameID인 속성을 기본 키로 해석합니다. 따라서 Student 클래스 기본 키의 자동으로 인식되는 대체 이름은 StudentID입니다. 자세한 내용은 EF Core- 키를 참조하세요.

Enrollments 속성은 탐색 속성입니다. 탐색 속성은 이 엔터티와 관련된 다른 엔터티를 포함합니다. 이 경우 Student 엔터티의 Enrollments 속성은 해당 Student에 관련된 모든 Enrollment 엔터티를 포함합니다. 예를 데이터베이스의 Student 행에 두 개의 관련 Enrollment 행이 있는 경우 Enrollments 탐색 속성은 그 두 Enrollment 엔터티를 포함합니다.

데이터베이스에서 StudentID 열에 학생 ID 값이 포함된 경우 Enrollment 행은 Student 행과 관련됩니다. 예를 들어 Student 행에 ID=1이 있다고 가정합니다. 관련 Enrollment 행에는 StudentID = 1이 포함됩니다. StudentID등록 테이블의 외래 키 입니다.

관련 Enrollment 엔터티가 여러 개 있을 수 있으므로 Enrollments 속성은 ICollection<Enrollment>로 정의됩니다. List<Enrollment> 또는 HashSet<Enrollment>와 같은 다른 컬렉션 형식을 사용할 수 있습니다. ICollection<Enrollment>이 사용되는 경우, EF Core는 기본적으로 HashSet<Enrollment> 컬렉션을 만듭니다.

등록 엔터티

Enrollment entity diagram

다음 코드를 사용하여 만듭니 Models/Enrollment.cs 다.

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

EnrollmentID 속성은 기본 키입니다. 이 엔터티는 자체적으로 ID 대신 classnameID 패턴을 사용합니다. 프로덕션 데이터 모델의 경우 많은 개발자들이 하나의 패턴을 선택하고 일관되게 사용합니다. 이 자습서에서는 두 항목이 작동하는 것을 보여 주기 위해 둘 다 사용합니다. classname 없이 ID를 사용하면 특정 종류의 데이터 모델 변경 내용을 더 쉽게 구현할 수 있습니다.

Grade 속성은 enum입니다. Grade 형식 선언 뒤에 있는 물음표는 Grade 속성이 nullable이라는 것을 나타냅니다. Null인 등급은 0 등급과는 다릅니다. Null은 알려지지 않거나 아직 등록되지 않은 등급을 의미합니다.

StudentID 속성은 외래 키로, 해당 탐색 속성은 Student입니다. Enrollment 엔터티는 하나의 Student 엔터티와 연결되므로 속성은 단일 Student 엔터티를 포함합니다.

CourseID 속성은 외래 키로, 해당 탐색 속성은 Course입니다. Enrollment 엔터티는 하나의 Course 엔터티와 연결됩니다.

EF Core는 속성 이름이 <navigation property name><primary key property name>인 경우 외래 키로 해석합니다. 예를 들어 Student 엔터티의 기본 키는 ID이므로 StudentIDStudent 탐색 속성의 외래 키입니다. 외래 키 속성의 이름은 <primary key property name>으로 지정할 수 있습니다. 예를 들어 Course 엔터티의 기본 키가 CourseID이므로 CourseID라고 할 수 있습니다.

강좌 엔터티

Course entity diagram

다음 코드를 사용하여 만듭니 Models/Course.cs 다.

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

Enrollments 속성은 탐색 속성입니다. Course 엔터티는 Enrollment 엔터티의 개수와 관련이 있을 수 있습니다.

DatabaseGenerated 특성을 사용하면 데이터베이스에서 기본 키를 생성하도록 하지 않고 앱에서 기본 키를 지정할 수 있습니다.

앱을 빌드합니다. 컴파일러는 null 값이 처리되는 방법에 대한 몇 가지 경고를 생성합니다. 자세한 내용은 이 GitHub 문제, nullable 참조 형식자습서: nullable 참조 형식 및 nullable이 아닌 참조 형식을 사용하여 디자인 의도를 보다 명확하게 표현을 참조하세요.

Null 허용 참조 형식의 경고를 제거하려면 ContosoUniversity.csproj 파일에서 다음 줄을 제거합니다.

<Nullable>enable</Nullable>

스캐폴딩 엔진은 현재 nullable 참조 형식을 지원하지 않기 때문에 스캐폴드에 사용되는 모델도 지원할 수 없습니다.

Pages/Error.cshtml.cspublic string? RequestId { get; set; }에서 ? nullable 참조 형식 주석을 제거합니다. 그러면 프로젝트가 컴파일러 경고 없이 빌드됩니다.

학생 페이지 스캐폴드

이 섹션에서는 ASP.NET Core 스캐폴딩 도구를 사용하여 다음을 생성합니다.

  • EF CoreDbContext 클래스. 컨텍스트는 특정 데이터 모델에 맞게 Entity Framework 기능을 조정하는 주 클래스입니다. 이 클래스는 Microsoft.EntityFrameworkCore.DbContext 클래스에서 파생됩니다.
  • Student 엔터티에 대한 CRUD(만들기, 읽기, 업데이트 및 삭제) 작업을 처리하는 Razor 페이지.
  • Pages/Students 폴더를 만듭니다.
  • 솔루션 탐색기에서 Pages/Students 폴더를 마우스 오른쪽 단추로 클릭하고 추가>새 스캐폴드된 항목을 선택합니다.
  • 새 스캐폴드 항목 추가 대화 상자에서 다음을 수행합니다.
    • 왼쪽 탭에서 설치 > 공통 >Razor Pages를 선택합니다.
    • Entity Framework를 사용한 Razor Razor Pages(CRUD)>추가를 선택합니다.
  • CRUD(Entity Framework)를 사용하여 페이지 추가 Razor 대화 상자에서 다음을 수행합니다.
    • 모델 클래스 드롭다운에서 학생(ContosoUniversity.Models)을 선택합니다.
    • 데이터 컨텍스트 클래스 행에서 +(더하기) 기호를 선택합니다.
      • 데이터 컨텍스트 이름을 ContosoUniversityContext가 아닌 SchoolContext로 끝나게 변경합니다. 업데이트된 컨텍스트 이름: ContosoUniversity.Data.SchoolContext
      • 추가를 선택하여 데이터 컨텍스트 클래스 추가를 마칩니다.
      • 추가를 선택하여 Razor 페이지 추가 대화 상자를 마칩니다.

다음 패키지가 자동으로 설치됩니다.

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

앞의 단계가 실패할 경우 프로젝트를 빌드하고 스캐폴드 단계를 다시 시도합니다.

스캐폴딩 프로세스:

  • Pages/Students 폴더에 페이지를 만듭니다Razor.
    • Create.cshtmlCreate.cshtml.cs
    • Delete.cshtmlDelete.cshtml.cs
    • Details.cshtmlDetails.cshtml.cs
    • Edit.cshtmlEdit.cshtml.cs
    • Index.cshtmlIndex.cshtml.cs
  • Data/SchoolContext.cs를 만듭니다.
  • 에서 종속성 주입에 컨텍스트를 Program.cs추가합니다.
  • appsettings.json에 데이터베이스 연결 문자열을 추가합니다.

데이터베이스 연결 문자열

스캐폴딩 도구가 appsettings.json 파일에 연결 문자열을 생성합니다.

연결 문자열은 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는 프로덕션 사용이 아닌 앱 개발용으로 고안된 SQL Server Express 데이터베이스 엔진의 경량 버전입니다. 기본적으로 LocalDB는 C:/Users/<user> 디렉터리에 .mdf 파일을 만듭니다.

데이터베이스 컨텍스트 클래스 업데이트

지정된 데이터 모델의 EF Core 기능을 조정하는 주 클래스는 데이터베이스 컨텍스트 클래스입니다. 컨텍스트는 Microsoft.EntityFrameworkCore.DbContext에서 파생됩니다. 컨텍스트는 데이터 모델에 포함되는 엔터티를 지정합니다. 이 프로젝트에서 클래스 이름은 SchoolContext로 지정됩니다.

다음 코드를 사용하여 Data/SchoolContext.cs을 업데이트합니다.

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

앞의 코드가 단수 DbSet<Student> Student에서 복수 DbSet<Student> Students로 변경됩니다. Pages 코드가 Razor 새 DBSet 이름과 일치하도록 하려면 다음에서 전역으로 변경합니다. _context.Student.에서 다음으로 전체적으로 변경: _context.Students.

8개 발생 항목이 있습니다.

엔터티 집합은 여러 엔터티를 포함하기 때문에 다수의 개발자들이 복수인 DBSet 속성 이름을 선호합니다.

강조 표시된 코드:

  • 각 엔터티 집합에 대한 DbSet<TEntity> 속성을 만듭니다. EF Core 용어:
    • 엔터티 집합은 일반적으로 데이터베이스 테이블에 해당합니다.
    • 엔터티는 테이블의 행에 해당합니다.
  • OnModelCreating. OnModelCreating:
    • SchoolContext가 초기화되었을 때 모델이 잠기고 컨텍스트를 초기화하는 데 사용되기 전에 호출됩니다.
    • 자습서의 뒷부분에서 Student 엔터티가 다른 엔터티에 대한 참조를 포함하기 때문에 필요합니다.

이후 릴리스에서 이 문제를 해결하려고 노력 중입니다.

Program.cs

ASP.NET Core는 종속성 주입을 사용하여 빌드됩니다. 서비스(예: SchoolContext)는 앱 시작 중에 종속성 주입에 등록됩니다. 이러한 서비스(예: Razor Pages)가 필요한 구성 요소는 생성자 매개 변수를 통해 해당 서비스를 제공받습니다. 데이터베이스 컨텍스트 인스턴스를 가져오는 생성자 코드는 자습서 뒷부분에 나옵니다.

스캐폴딩 도구는 종속성 주입 컨테이너에 컨텍스트 클래스를 자동으로 등록합니다.

다음 강조 표시된 줄을 스캐폴더가 추가했습니다.

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

DbContextOptions 개체에서 메서드를 호출하여 연결 문자열의 이름을 컨텍스트에 전달합니다. 로컬 개발의 경우 ASP.NET Core 구성 시스템appsettings.json 또는 appsettings.Development.json 파일에서 연결 문자열을 읽습니다.

데이터베이스 예외 필터 추가

다음 코드와 같이 AddDatabaseDeveloperPageExceptionFilterUseMigrationsEndPoint를 추가합니다.

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

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet 패키지를 추가합니다.

패키지 관리자 콘솔에서 다음을 입력하여 NuGet 패키지를 추가합니다.

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet 패키지는 Entity Framework Core 오류 페이지에 대한 ASP.NET Core 미들웨어를 제공합니다. 이 미들웨어는 Entity Framework Core 마이그레이션의 오류를 검색 및 진단하는 데 도움이 됩니다.

AddDatabaseDeveloperPageExceptionFilter개발 환경에서 EF 마이그레이션 오류에 대한 유용한 오류 정보를 제공합니다.

데이터베이스 생성

데이터베이스가 없는 경우 데이터베이스를 만들도록 업데이트 Program.cs 합니다.

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

EnsureCreated 컨텍스트에 대한 데이터베이스가 있는 경우 메서드는 아무 작업도 수행하지 않습니다. 데이터베이스가 없는 경우 데이터베이스 및 스키마를 만듭니다. EnsureCreated를 사용하면 다음 워크플로가 데이터 모델 변경 내용을 처리할 수 있습니다.

  • 데이터베이스를 삭제합니다. 모든 기존 데이터가 손실됩니다.
  • 데이터 모델을 변경합니다. 예를 들어 EmailAddress 필드를 추가합니다.
  • 앱을 실행합니다.
  • EnsureCreated는 새 스키마를 사용하여 데이터베이스를 만듭니다.

이 워크플로는 데이터를 보존할 필요가 없는 경우 스키마가 빠르게 업데이트되는 개발 초기에 적합합니다. 데이터베이스에 입력된 데이터를 보존해야 하는 경우는 상황이 다릅니다. 이 경우에는 마이그레이션을 사용합니다.

자습서 시리즈의 후반부에서는 EnsureCreated에서 만든 데이터베이스가 삭제되고 마이그레이션이 사용됩니다. EnsureCreated에서 만든 데이터베이스는 마이그레이션을 사용하여 업데이트할 수 없습니다.

앱 테스트

  • 앱을 실행합니다.
  • 학생 링크 및 새로 만들기를 차례로 선택합니다.
  • Edit, Details 및 Delete 링크를 테스트합니다.

데이터베이스 시드

EnsureCreated 메서드는 빈 데이터베이스를 만듭니다. 이 섹션에서는 테스트 데이터로 데이터베이스를 채우는 코드를 추가합니다.

다음 코드를 사용하여 만듭니 Data/DbInitializer.cs 다.

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

이 코드는 데이터베이스에 학생이 있는지 확인합니다. 학생이 없는 경우 테스트 데이터를 데이터베이스에 추가합니다. List<T> 컬렉션이 아닌 배열에 테스트 데이터를 만들어 성능을 최적화합니다.

  • Program.cs에서 DbInitializer.Initialize 줄에 있는 //를 제거합니다.
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<SchoolContext>();
    context.Database.EnsureCreated();
    DbInitializer.Initialize(context);
}
  • 앱이 실행 중이라면 중지하고 패키지 관리자 콘솔(PMC)에서 다음 명령을 실행합니다.

    Drop-Database -Confirm
    
    
  • 데이터베이스를 삭제하려면 Y로 답합니다.

  • 앱을 다시 시작합니다.
  • 학생 페이지를 선택하여 시드된 데이터를 확인합니다.

데이터베이스 보기

  • Visual Studio의 보기 메뉴에서 SSOX(SQL Server 개체 탐색기)를 엽니다.
  • SSOX에서 (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}를 선택합니다. 데이터베이스 이름은 이전에 제공한 컨텍스트 이름과 대시 및 GUID를 사용하여 생성됩니다.
  • 테이블 노드를 확장합니다.
  • 학생 테이블을 마우스 오른쪽 단추로 클릭하고, 데이터 보기를 클릭하여 만든 열 및 테이블에 삽입된 행을 볼 수 있습니다.
  • Student 테이블을 마우스 오른쪽 단추로 클릭하고 코드 보기를 클릭하여 Student 모델이 Student 테이블 스키마에 어떻게 매핑되는지 확인합니다.

ASP.NET Core 웹앱의 비동기 EF 메서드

비동기 프로그래밍은 ASP.NET Core 및 EF Core에 대한 기본 모드입니다.

웹 서버에는 사용할 수 있는 스레드 수가 제한적이며, 로드 양이 많은 상황에서는 사용 가능한 모든 스레드가 사용될 수 있습니다. 이 경우 서버는 스레드가 해제될 때까지 새 요청을 처리할 수 없습니다. 동기 코드를 사용하면 I/O 완료를 대기하느라 작업을 수행하지 않는 동안에 많은 스레드가 정체될 수 있습니다. 비동기 코드를 사용하면 프로세스가 I/O 완료를 대기할 때 다른 요청을 처리하는 데 사용하도록 해당 스레드가 서버에서 해제됩니다. 따라서 비동기 코드를 사용하면 서버 리소스를 더 효율적으로 사용할 수 있으며 서버가 지연 없이 더 많은 트래픽을 처리할 수 있습니다.

비동기 코드는 런타임 시 약간의 오버헤드를 도입합니다. 트래픽이 높은 상황에서는 잠재적 성능 향상이 상당한 반면, 트래픽이 낮은 상황의 경우 성능 저하는 미미합니다.

다음 코드에서 async 키워드, Task 반환 값, await 키워드 및 ToListAsync 메서드는 비동기적으로 코드 실행을 수행합니다.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • async 키워드(keyword) 컴파일러에 다음을 지시합니다.
    • 메서드 본문의 부분에 대한 콜백을 생성합니다.
    • 반환되는 Task 개체를 만듭니다.
  • Task 반환 형식은 진행 중인 작업을 나타냅니다.
  • await 키워드로 인해 컴파일러는 메서드를 두 부분으로 분할합니다. 첫 번째 부분은 비동기적으로 시작되는 작업을 종료합니다. 두 번째 부분은 작업이 완료될 때 호출되는 콜백 메서드에 배치됩니다.
  • ToListAsyncToList 확장 메서드의 비동기 버전입니다.

EF Core를 사용하는 비동기 코드를 작성할 때 주의 사항:

  • 쿼리 또는 명령을 데이터베이스에 보내는 명령문만 비동기적으로 실행됩니다. 여기에는 ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsyncSaveChangesAsync가 포함됩니다. var students = context.Students.Where(s => s.LastName == "Davolio")와 같은 IQueryable을 변경하는 명령문은 포함되지 않습니다.
  • EF Core 컨텍스트는 스레드로부터 안전하지 않습니다. 동시에 여러 작업을 수행하지 마세요.
  • 비동기 코드의 성능 이점을 이용하려면 라이브러리 패키지(예: 페이징)가 쿼리를 데이터베이스에 보내는 EF Core 메서드를 호출하는 경우 비동기를 사용하는지 확인합니다.

.NET에서의 비동기 프로그래밍에 대한 자세한 내용은 비동기 개요Async 및 Await를 사용한 비동기 프로그래밍을 참조하세요.

Warning

Microsoft.Data.SqlClient의 비동기 구현에는 알려진 문제(#593, #601 등)가 있습니다. 예기치 않은 성능 문제가 발생하는 경우 특히 큰 텍스트 또는 이진 값을 처리할 때 동기화 명령 실행을 대신 사용해 보세요.

성능 고려 사항

일반적으로 웹 페이지는 임의의 행 수를 로드하지 않아야 합니다. 쿼리는 페이징이나 제한 방법을 사용해야 합니다. 예를 들어 앞의 쿼리는 Take를 사용하여 반환되는 행을 제한할 수 있습니다.

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

부분적으로 열거를 통해 데이터베이스 예외가 발생할 경우, 뷰에서 대규모 테이블을 열거하면 일부분이 생성된 HTTP 200 응답을 반환할 수 있습니다.

페이징에 대해서는 자습서 뒷부분에서 다룹니다.

자세한 내용은 성능 고려 사항(EF)을 참조하세요.

다음 단계

개발에 SQLite 사용, 프로덕션용 SQL Server

ASP.NET Core Razor Pages 앱에서 EF(Entity Framework) Core를 사용하는 방법을 보여 주는 자습서 시리즈 중 첫 번째 자습서입니다. 이 자습서에서는 가상 Contoso 대학의 웹 사이트를 빌드합니다. 이 사이트에는 학생 입학, 강좌 개설 및 강사 할당과 같은 기능이 있습니다. 이 자습서에서는 Code First 방법을 사용합니다. Database First 방법을 사용하여 이 자습서를 수행하는 데 대한 정보는 이 Github 문제를 참조하세요.

완료된 앱을 다운로드하거나 봅니다.지침을 다운로드합니다.

필수 조건

  • Razor Pages를 처음 사용한다면 이 자습서를 시작하기 전에 Razor Razor Pages 시작 자습서 시리즈를 진행하세요.

데이터베이스 엔진

Visual Studio 지침에서는 Windows에서만 실행되는 SQL Server Express 버전인 SQL Server LocalDB를 사용합니다.

문제 해결

해결할 수 없는 문제가 발생한 경우 완료된 프로젝트와 코드를 비교합니다. 도움을 얻으려면 ASP.NET Core 태그 또는 EF Core태그를 사용하여 StackOverflow.com에 질문을 게시하는 것이 좋습니다.

샘플 앱

이러한 자습서에서 빌드한 앱은 기본 대학 웹 사이트입니다. 사용자는 학생, 강좌 및 강사 정보를 보고 업데이트할 수 있습니다. 자습서에서 만든 화면 중 몇 가지 예가 나와 있습니다.

Students Index page

Students Edit page

이 사이트의 UI 스타일은 기본 제공 프로젝트 템플릿을 기반으로 합니다. 이 자습서에서는 UI를 사용자 지정하는 방법이 아닌 EF Core를 ASP.NET Core와 함께 사용하는 방법을 중점적으로 설명합니다.

선택 사항: 샘플 다운로드 빌드

이 단계는 옵션입니다. 해결할 수 없는 문제가 발생하는 경우 완성된 앱을 빌드하는 것이 좋습니다. 해결할 수 없는 문제가 발생한 경우 완료된 프로젝트와 코드를 비교합니다. 지침을 다운로드하세요.

ContosoUniversity.csproj을 선택하여 프로젝트를 엽니다.

  • 프로젝트를 빌드합니다.
  • PMC(패키지 관리자 콘솔)에서 다음 명령을 실행합니다.
Update-Database

프로젝트를 실행하여 데이터베이스를 시드합니다.

웹앱 프로젝트 만들기

  1. Visual Studio를 시작하고 새 프로젝트 만들기를 선택합니다.
  2. 새 프로젝트 만들기 대화 상자에서 ASP.NET Core 웹 애플리케이션>다음을 선택합니다.
  3. 새 프로젝트 구성 대화 상자에서 프로젝트 이름으로 ContosoUniversity를 입력합니다. 코드를 복사할 때 namespace가 일치하도록 대문자 표시를 포함하여 이 이름을 정확히 사용해야 합니다.
  4. 만들기를 실행합니다.
  5. 새 ASP.NET Core 웹 애플리케이션 만들기 대화 상자에서 다음을 선택합니다.
    1. 드롭다운에서 .NET CoreASP.NET Core 5.0을 선택합니다.
    2. ASP.NET Core 웹앱
    3. 만들기New ASP.NET Core Project dialog

사이트 스타일 설정

다음 코드를 복사하여 Pages/Shared/_Layout.cshtml 파일에 붙여넣습니다.

<!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>

레이아웃 파일은 사이트 머리글, 바닥글 및 메뉴를 설정합니다. 위의 코드로 다음이 변경됩니다.

  • 모든 “ContosoUniversity”가 “Contoso University”로 변경됩니다. 세 번 나옵니다.
  • HomePrivacy 메뉴 항목이 삭제됩니다.
  • 정보,학생, 과정, 강사부서에 대해 항목이 추가됩니다.

Pages/Index.cshtml에서 파일의 콘텐츠를 다음 코드로 바꿉니다.

@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>

위의 코드는 ASP.NET Core에 대한 텍스트를 이 앱에 대한 텍스트로 바꿉니다.

앱을 실행하여 홈페이지가 표시되는지 확인합니다.

데이터 모델

다음 섹션에서는 데이터 모델을 만듭니다.

Course-Enrollment-Student data model diagram

학생은 개수와 관계없이 원하는 과정에 등록할 수 있으며 한 과정에는 여러 명이 등록될 수 있습니다.

학생 엔터티

Student entity diagram

  • 프로젝트 폴더에 Models 폴더를 만듭니다.

  • 다음 코드를 사용하여 만듭니 Models/Student.cs 다.

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

ID 속성은 이 클래스에 해당하는 데이터베이스 테이블의 기본 키 열이 됩니다. 기본적으로 EF Core는 이름이 ID 또는 classnameID인 속성을 기본 키로 해석합니다. 따라서 Student 클래스 기본 키의 자동으로 인식되는 대체 이름은 StudentID입니다. 자세한 내용은 EF Core- 키를 참조하세요.

Enrollments 속성은 탐색 속성입니다. 탐색 속성은 이 엔터티와 관련된 다른 엔터티를 포함합니다. 이 경우 Student 엔터티의 Enrollments 속성은 해당 Student에 관련된 모든 Enrollment 엔터티를 포함합니다. 예를 데이터베이스의 Student 행에 두 개의 관련 Enrollment 행이 있는 경우 Enrollments 탐색 속성은 그 두 Enrollment 엔터티를 포함합니다.

데이터베이스에서 StudentID 열에 학생 ID 값이 포함된 경우 Enrollment 행은 Student 행과 관련됩니다. 예를 들어 Student 행에 ID=1이 있다고 가정합니다. 관련 Enrollment 행에는 StudentID = 1이 포함됩니다. StudentID등록 테이블의 외래 키 입니다.

관련 Enrollment 엔터티가 여러 개 있을 수 있으므로 Enrollments 속성은 ICollection<Enrollment>로 정의됩니다. List<Enrollment> 또는 HashSet<Enrollment>와 같은 다른 컬렉션 형식을 사용할 수 있습니다. ICollection<Enrollment>이 사용되는 경우, EF Core는 기본적으로 HashSet<Enrollment> 컬렉션을 만듭니다.

등록 엔터티

Enrollment entity diagram

다음 코드를 사용하여 만듭니 Models/Enrollment.cs 다.

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

EnrollmentID 속성은 기본 키입니다. 이 엔터티는 자체적으로 ID 대신 classnameID 패턴을 사용합니다. 프로덕션 데이터 모델의 경우 많은 개발자들이 하나의 패턴을 선택하고 일관되게 사용합니다. 이 자습서에서는 두 항목이 작동하는 것을 보여 주기 위해 둘 다 사용합니다. classname 없이 ID를 사용하면 특정 종류의 데이터 모델 변경 내용을 더 쉽게 구현할 수 있습니다.

Grade 속성은 enum입니다. Grade 형식 선언 뒤에 있는 물음표는 Grade 속성이 nullable이라는 것을 나타냅니다. Null인 등급은 0 등급과는 다릅니다. Null은 알려지지 않거나 아직 등록되지 않은 등급을 의미합니다.

StudentID 속성은 외래 키로, 해당 탐색 속성은 Student입니다. Enrollment 엔터티는 하나의 Student 엔터티와 연결되므로 속성은 단일 Student 엔터티를 포함합니다.

CourseID 속성은 외래 키로, 해당 탐색 속성은 Course입니다. Enrollment 엔터티는 하나의 Course 엔터티와 연결됩니다.

EF Core는 속성 이름이 <navigation property name><primary key property name>인 경우 외래 키로 해석합니다. 예를 들어 Student 엔터티의 기본 키는 ID이므로 StudentIDStudent 탐색 속성의 외래 키입니다. 외래 키 속성의 이름은 <primary key property name>으로 지정할 수 있습니다. 예를 들어 Course 엔터티의 기본 키가 CourseID이므로 CourseID라고 할 수 있습니다.

강좌 엔터티

Course entity diagram

다음 코드를 사용하여 만듭니 Models/Course.cs 다.

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

Enrollments 속성은 탐색 속성입니다. Course 엔터티는 Enrollment 엔터티의 개수와 관련이 있을 수 있습니다.

DatabaseGenerated 특성을 사용하면 데이터베이스에서 기본 키를 생성하도록 하지 않고 앱에서 기본 키를 지정할 수 있습니다.

프로젝트를 빌드하여 컴파일러 오류가 없는지 유효성을 검사합니다.

학생 페이지 스캐폴드

이 섹션에서는 ASP.NET Core 스캐폴딩 도구를 사용하여 다음을 생성합니다.

  • EF CoreDbContext 클래스. 컨텍스트는 특정 데이터 모델에 맞게 Entity Framework 기능을 조정하는 주 클래스입니다. 이 클래스는 Microsoft.EntityFrameworkCore.DbContext 클래스에서 파생됩니다.
  • Student 엔터티에 대한 CRUD(만들기, 읽기, 업데이트 및 삭제) 작업을 처리하는 Razor 페이지.
  • Pages/Students 폴더를 만듭니다.
  • 솔루션 탐색기에서 Pages/Students 폴더를 마우스 오른쪽 단추로 클릭하고 추가>새 스캐폴드된 항목을 선택합니다.
  • 새 스캐폴드 항목 추가 대화 상자에서 다음을 수행합니다.
    • 왼쪽 탭에서 설치 > 공통 >Razor Pages를 선택합니다.
    • Entity Framework를 사용한 Razor Razor Pages(CRUD)>추가를 선택합니다.
  • CRUD(Entity Framework)를 사용하여 페이지 추가 Razor 대화 상자에서 다음을 수행합니다.
    • 모델 클래스 드롭다운에서 학생(ContosoUniversity.Models)을 선택합니다.
    • 데이터 컨텍스트 클래스 행에서 +(더하기) 기호를 선택합니다.
      • 데이터 컨텍스트 이름을 ContosoUniversityContext가 아닌 SchoolContext로 끝나게 변경합니다. 업데이트된 컨텍스트 이름: ContosoUniversity.Data.SchoolContext
      • 추가를 선택하여 데이터 컨텍스트 클래스 추가를 마칩니다.
      • 추가를 선택하여 Razor 페이지 추가 대화 상자를 마칩니다.

오류 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.'를 나타내며 스캐폴딩이 실패하면 스캐폴드 도구를 다시 실행하거나 이 GitHub 문제를 확인하세요.

다음 패키지가 자동으로 설치됩니다.

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

앞의 단계가 실패할 경우 프로젝트를 빌드하고 스캐폴드 단계를 다시 시도합니다.

스캐폴딩 프로세스:

  • Pages/Students 폴더에 페이지를 만듭니다Razor.
    • Create.cshtmlCreate.cshtml.cs
    • Delete.cshtmlDelete.cshtml.cs
    • Details.cshtmlDetails.cshtml.cs
    • Edit.cshtmlEdit.cshtml.cs
    • Index.cshtmlIndex.cshtml.cs
  • Data/SchoolContext.cs를 만듭니다.
  • 에서 종속성 주입에 컨텍스트를 Startup.cs추가합니다.
  • appsettings.json에 데이터베이스 연결 문자열을 추가합니다.

데이터베이스 연결 문자열

스캐폴딩 도구가 appsettings.json 파일에 연결 문자열을 생성합니다.

연결 문자열은 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는 프로덕션 사용이 아닌 앱 개발용으로 고안된 SQL Server Express 데이터베이스 엔진의 경량 버전입니다. 기본적으로 LocalDB는 C:/Users/<user> 디렉터리에 .mdf 파일을 만듭니다.

데이터베이스 컨텍스트 클래스 업데이트

지정된 데이터 모델의 EF Core 기능을 조정하는 주 클래스는 데이터베이스 컨텍스트 클래스입니다. 컨텍스트는 Microsoft.EntityFrameworkCore.DbContext에서 파생됩니다. 컨텍스트는 데이터 모델에 포함되는 엔터티를 지정합니다. 이 프로젝트에서 클래스 이름은 SchoolContext로 지정됩니다.

다음 코드를 사용하여 Data/SchoolContext.cs을 업데이트합니다.

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

앞의 코드가 단수 DbSet<Student> Student에서 복수 DbSet<Student> Students로 변경됩니다. Pages 코드가 Razor 새 DBSet 이름과 일치하도록 하려면 다음에서 전역으로 변경합니다. _context.Student.에서 다음으로 전체적으로 변경: _context.Students.

8개 발생 항목이 있습니다.

엔터티 집합은 여러 엔터티를 포함하기 때문에 다수의 개발자들이 복수인 DBSet 속성 이름을 선호합니다.

강조 표시된 코드:

  • 각 엔터티 집합에 대한 DbSet<TEntity> 속성을 만듭니다. EF Core 용어:
    • 엔터티 집합은 일반적으로 데이터베이스 테이블에 해당합니다.
    • 엔터티는 테이블의 행에 해당합니다.
  • OnModelCreating. OnModelCreating:
    • SchoolContext가 초기화되었을 때 모델이 잠기고 컨텍스트를 초기화하는 데 사용되기 전에 호출됩니다.
    • 자습서의 뒷부분에서 Student 엔터티가 다른 엔터티에 대한 참조를 포함하기 때문에 필요합니다.

프로젝트를 빌드하여 컴파일러 오류가 없는지 확인합니다.

Startup.cs

ASP.NET Core는 종속성 주입을 사용하여 빌드됩니다. 서비스(예: SchoolContext)는 앱 시작 중에 종속성 주입에 등록됩니다. 이러한 서비스(예: Razor Pages)가 필요한 구성 요소는 생성자 매개 변수를 통해 해당 서비스를 제공받습니다. 데이터베이스 컨텍스트 인스턴스를 가져오는 생성자 코드는 자습서 뒷부분에 나옵니다.

스캐폴딩 도구는 종속성 주입 컨테이너에 컨텍스트 클래스를 자동으로 등록합니다.

다음 강조 표시된 줄을 스캐폴더가 추가했습니다.

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

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

DbContextOptions 개체에서 메서드를 호출하여 연결 문자열의 이름을 컨텍스트에 전달합니다. 로컬 개발의 경우 ASP.NET Core 구성 시스템appsettings.json 파일에서 연결 문자열을 읽습니다.

데이터베이스 예외 필터 추가

다음 코드와 같이 AddDatabaseDeveloperPageExceptionFilterUseMigrationsEndPoint를 추가합니다.

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

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet 패키지를 추가합니다.

패키지 관리자 콘솔에서 다음을 입력하여 NuGet 패키지를 추가합니다.

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet 패키지는 Entity Framework Core 오류 페이지에 대한 ASP.NET Core 미들웨어를 제공합니다. 이 미들웨어는 Entity Framework Core 마이그레이션의 오류를 검색 및 진단하는 데 도움이 됩니다.

AddDatabaseDeveloperPageExceptionFilter개발 환경에서 EF 마이그레이션 오류에 대한 유용한 오류 정보를 제공합니다.

데이터베이스 생성

데이터베이스가 없는 경우 데이터베이스를 만들도록 업데이트 Program.cs 합니다.

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

EnsureCreated 컨텍스트에 대한 데이터베이스가 있는 경우 메서드는 아무 작업도 수행하지 않습니다. 데이터베이스가 없는 경우 데이터베이스 및 스키마를 만듭니다. EnsureCreated를 사용하면 다음 워크플로가 데이터 모델 변경 내용을 처리할 수 있습니다.

  • 데이터베이스를 삭제합니다. 모든 기존 데이터가 손실됩니다.
  • 데이터 모델을 변경합니다. 예를 들어 EmailAddress 필드를 추가합니다.
  • 앱을 실행합니다.
  • EnsureCreated는 새 스키마를 사용하여 데이터베이스를 만듭니다.

이 워크플로는 데이터를 보존할 필요가 없는 경우 스키마가 빠르게 업데이트되는 개발 초기에 적합합니다. 데이터베이스에 입력된 데이터를 보존해야 하는 경우는 상황이 다릅니다. 이 경우에는 마이그레이션을 사용합니다.

자습서 시리즈의 후반부에서는 EnsureCreated에서 만든 데이터베이스가 삭제되고 마이그레이션이 사용됩니다. EnsureCreated에서 만든 데이터베이스는 마이그레이션을 사용하여 업데이트할 수 없습니다.

앱 테스트

  • 앱을 실행합니다.
  • 학생 링크 및 새로 만들기를 차례로 선택합니다.
  • Edit, Details 및 Delete 링크를 테스트합니다.

데이터베이스 시드

EnsureCreated 메서드는 빈 데이터베이스를 만듭니다. 이 섹션에서는 테스트 데이터로 데이터베이스를 채우는 코드를 추가합니다.

다음 코드를 사용하여 만듭니 Data/DbInitializer.cs 다.

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

이 코드는 데이터베이스에 학생이 있는지 확인합니다. 학생이 없는 경우 테스트 데이터를 데이터베이스에 추가합니다. List<T> 컬렉션이 아닌 배열에 테스트 데이터를 만들어 성능을 최적화합니다.

  • Program.cs에서 DbInitializer.Initialize 줄에 있는 //를 제거합니다.

      context.Database.EnsureCreated();
      DbInitializer.Initialize(context);
    
  • 앱이 실행 중이라면 중지하고 패키지 관리자 콘솔(PMC)에서 다음 명령을 실행합니다.

    Drop-Database -Confirm
    
    
  • 데이터베이스를 삭제하려면 Y로 답합니다.

  • 앱을 다시 시작합니다.
  • 학생 페이지를 선택하여 시드된 데이터를 확인합니다.

데이터베이스 보기

  • Visual Studio의 보기 메뉴에서 SSOX(SQL Server 개체 탐색기)를 엽니다.
  • SSOX에서 (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}를 선택합니다. 데이터베이스 이름은 이전에 제공한 컨텍스트 이름과 대시 및 GUID를 사용하여 생성됩니다.
  • 테이블 노드를 확장합니다.
  • 학생 테이블을 마우스 오른쪽 단추로 클릭하고, 데이터 보기를 클릭하여 만든 열 및 테이블에 삽입된 행을 볼 수 있습니다.
  • Student 테이블을 마우스 오른쪽 단추로 클릭하고 코드 보기를 클릭하여 Student 모델이 Student 테이블 스키마에 어떻게 매핑되는지 확인합니다.

비동기 코드

비동기 프로그래밍은 ASP.NET Core 및 EF Core에 대한 기본 모드입니다.

웹 서버에는 사용할 수 있는 스레드 수가 제한적이며, 로드 양이 많은 상황에서는 사용 가능한 모든 스레드가 사용될 수 있습니다. 이 경우 서버는 스레드가 해제될 때까지 새 요청을 처리할 수 없습니다. 동기 코드를 사용하면 I/O 완료를 대기하느라 작업을 수행하지 않는 동안에 많은 스레드가 정체될 수 있습니다. 비동기 코드를 사용하면 프로세스가 I/O 완료를 대기할 때 다른 요청을 처리하는 데 사용하도록 해당 스레드가 서버에서 해제됩니다. 따라서 비동기 코드를 사용하면 서버 리소스를 더 효율적으로 사용할 수 있으며 서버가 지연 없이 더 많은 트래픽을 처리할 수 있습니다.

비동기 코드는 런타임 시 약간의 오버헤드를 도입합니다. 트래픽이 높은 상황에서는 잠재적 성능 향상이 상당한 반면, 트래픽이 낮은 상황의 경우 성능 저하는 미미합니다.

다음 코드에서 async 키워드, Task 반환 값, await 키워드 및 ToListAsync 메서드는 비동기적으로 코드 실행을 수행합니다.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • async 키워드(keyword) 컴파일러에 다음을 지시합니다.
    • 메서드 본문의 부분에 대한 콜백을 생성합니다.
    • 반환되는 Task 개체를 만듭니다.
  • Task 반환 형식은 진행 중인 작업을 나타냅니다.
  • await 키워드로 인해 컴파일러는 메서드를 두 부분으로 분할합니다. 첫 번째 부분은 비동기적으로 시작되는 작업을 종료합니다. 두 번째 부분은 작업이 완료될 때 호출되는 콜백 메서드에 배치됩니다.
  • ToListAsyncToList 확장 메서드의 비동기 버전입니다.

EF Core를 사용하는 비동기 코드를 작성할 때 주의 사항:

  • 쿼리 또는 명령을 데이터베이스에 보내는 명령문만 비동기적으로 실행됩니다. 여기에는 ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsyncSaveChangesAsync가 포함됩니다. var students = context.Students.Where(s => s.LastName == "Davolio")와 같은 IQueryable을 변경하는 명령문은 포함되지 않습니다.
  • EF Core 컨텍스트는 스레드로부터 안전하지 않습니다. 동시에 여러 작업을 수행하지 마세요.
  • 비동기 코드의 성능 이점을 이용하려면 라이브러리 패키지(예: 페이징)가 쿼리를 데이터베이스에 보내는 EF Core 메서드를 호출하는 경우 비동기를 사용하는지 확인합니다.

.NET에서의 비동기 프로그래밍에 대한 자세한 내용은 비동기 개요Async 및 Await를 사용한 비동기 프로그래밍을 참조하세요.

성능 고려 사항

일반적으로 웹 페이지는 임의의 행 수를 로드하지 않아야 합니다. 쿼리는 페이징이나 제한 방법을 사용해야 합니다. 예를 들어 앞의 쿼리는 Take를 사용하여 반환되는 행을 제한할 수 있습니다.

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

부분적으로 열거를 통해 데이터베이스 예외가 발생할 경우, 뷰에서 대규모 테이블을 열거하면 일부분이 생성된 HTTP 200 응답을 반환할 수 있습니다.

MaxModelBindingCollectionSize의 기본값은 1024입니다. 다음 코드는 MaxModelBindingCollectionSize를 설정합니다.

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

MyMaxModelBindingCollectionSize와 같은 구성 설정에 대한 내용은 구성을 참조하세요.

페이징에 대해서는 자습서 뒷부분에서 다룹니다.

자세한 내용은 성능 고려 사항(EF)을 참조하세요.

Entity Framework Core의 SQL 로깅

로깅 구성은 일반적으로 appsettings.{Environment}.json 파일의 Logging 섹션에서 제공됩니다. SQL 문을 로깅하려면 "Microsoft.EntityFrameworkCore.Database.Command": "Information"appsettings.Development.json 파일에 추가합니다.

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

위의 JSON을 사용하면 SQL 문이 명령줄과 Visual Studio 출력 창에 표시됩니다.

자세한 내용은 .NET Core 및 ASP.NET Core의 로깅 및 이 GitHub 이슈를 참조하세요.

다음 단계

개발에 SQLite 사용, 프로덕션용 SQL Server

ASP.NET Core Razor Pages 앱에서 EF(Entity Framework) Core를 사용하는 방법을 보여 주는 자습서 시리즈 중 첫 번째 자습서입니다. 이 자습서에서는 가상 Contoso 대학의 웹 사이트를 빌드합니다. 이 사이트에는 학생 입학, 강좌 개설 및 강사 할당과 같은 기능이 있습니다. 이 자습서에서는 Code First 방법을 사용합니다. Database First 방법을 사용하여 이 자습서를 수행하는 데 대한 정보는 이 Github 문제를 참조하세요.

완료된 앱을 다운로드하거나 봅니다.지침을 다운로드합니다.

필수 조건

  • Razor Pages를 처음 사용한다면 이 자습서를 시작하기 전에 Razor Razor Pages 시작 자습서 시리즈를 진행하세요.

데이터베이스 엔진

Visual Studio 지침에서는 Windows에서만 실행되는 SQL Server Express 버전인 SQL Server LocalDB를 사용합니다.

Visual Studio Code 지침에서는 플랫폼 간 데이터베이스 엔진인 SQLite를 사용합니다.

SQLite를 사용하도록 선택하는 경우 SQLite용 DB 브라우저와 같이 SQLite 데이터베이스를 관리하고 볼 수 있는 타사 도구를 다운로드하여 설치합니다.

문제 해결

해결할 수 없는 문제가 발생한 경우 완료된 프로젝트와 코드를 비교합니다. 도움을 얻으려면 ASP.NET Core 태그 또는 EF Core태그를 사용하여 StackOverflow.com에 질문을 게시하는 것이 좋습니다.

샘플 앱

이러한 자습서에서 빌드한 앱은 기본 대학 웹 사이트입니다. 사용자는 학생, 강좌 및 강사 정보를 보고 업데이트할 수 있습니다. 자습서에서 만든 화면 중 몇 가지 예가 나와 있습니다.

Students Index page

Students Edit page

이 사이트의 UI 스타일은 기본 제공 프로젝트 템플릿을 기반으로 합니다. 이 자습서에서는 UI를 사용자 지정하는 방법이 아닌 EF Core를 사용하는 방법을 중점적으로 설명합니다.

페이지 맨 위에 있는 링크를 따라 완료된 프로젝트의 소스 코드를 가져옵니다. cu30 폴더에는 자습서의 ASP.NET Core 3.0 버전에 대한 코드가 있습니다. 자습서 1~7의 코드 상태를 반영하는 파일은 cu30snapshots 폴더에서 찾을 수 있습니다.

완료된 프로젝트를 다운로드한 후 앱을 실행하려면 다음을 수행합니다.

  • 프로젝트를 빌드합니다.

  • PMC(패키지 관리자 콘솔)에서 다음 명령을 실행합니다.

    Update-Database
    
  • 프로젝트를 실행하여 데이터베이스를 시드합니다.

웹앱 프로젝트 만들기

  • Visual Studio 파일 메뉴에서 새로 만들기>프로젝트를 선택합니다.
  • 새 ASP.NET Core 웹 애플리케이션을 선택합니다.
  • 프로젝트 이름을 ContosoUniversity로 지정합니다. 코드를 복사하여 붙여넣을 때 네임스페이스가 일치하도록 대문자 표시를 포함하여 정확한 이름을 사용해야 합니다.
  • 드롭다운에서 .NET CoreASP.NET Core 3.0을 선택한 후 웹 애플리케이션을 선택합니다.

사이트 스타일 설정

다음을 업데이트 Pages/Shared/_Layout.cshtml하여 사이트 머리글, 바닥글 및 메뉴를 설정합니다.

  • 모든 “ContosoUniversity”를 “Contoso University”로 변경합니다. 세 번 나옵니다.

  • HomePrivacy 메뉴 항목을 삭제하고 정보, 학생, 과정, 강사부서 항목을 추가합니다.

변경 내용은 강조 표시되어 있습니다.

<!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>

에서 Pages/Index.cshtml파일의 내용을 다음 코드로 바꿔서 ASP.NET Core에 대한 텍스트를 이 앱에 대한 텍스트로 바꿉니다.

@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>

앱을 실행하여 홈페이지가 표시되는지 확인합니다.

데이터 모델

다음 섹션에서는 데이터 모델을 만듭니다.

Course-Enrollment-Student data model diagram

학생은 개수와 관계없이 원하는 과정에 등록할 수 있으며 한 과정에는 여러 명이 등록될 수 있습니다.

학생 엔터티

Student entity diagram

  • 프로젝트 폴더에 Models 폴더를 만듭니다.

  • 다음 코드를 사용하여 만듭니 Models/Student.cs 다.

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

ID 속성은 이 클래스에 해당하는 데이터베이스 테이블의 기본 키 열이 됩니다. 기본적으로 EF Core는 이름이 ID 또는 classnameID인 속성을 기본 키로 해석합니다. 따라서 Student 클래스 기본 키의 자동으로 인식되는 대체 이름은 StudentID입니다. 자세한 내용은 EF Core- 키를 참조하세요.

Enrollments 속성은 탐색 속성입니다. 탐색 속성은 이 엔터티와 관련된 다른 엔터티를 포함합니다. 이 경우 Student 엔터티의 Enrollments 속성은 해당 Student에 관련된 모든 Enrollment 엔터티를 포함합니다. 예를 데이터베이스의 Student 행에 두 개의 관련 Enrollment 행이 있는 경우 Enrollments 탐색 속성은 그 두 Enrollment 엔터티를 포함합니다.

데이터베이스에서 StudentID 열에 학생 ID 값이 포함된 경우 Enrollment 행은 Student 행과 관련됩니다. 예를 들어 Student 행에 ID=1이 있다고 가정합니다. 관련 Enrollment 행에는 StudentID=1이 포함됩니다. StudentID는 Enrollment 테이블의 ‘외래 키’입니다.

관련 Enrollment 엔터티가 여러 개 있을 수 있으므로 Enrollments 속성은 ICollection<Enrollment>로 정의됩니다. List<Enrollment> 또는HashSet<Enrollment>와 같은 다른 컬렉션 형식을 사용할 수 있습니다. ICollection<Enrollment>이 사용되는 경우, EF Core는 기본적으로 HashSet<Enrollment> 컬렉션을 만듭니다.

등록 엔터티

Enrollment entity diagram

다음 코드를 사용하여 만듭니 Models/Enrollment.cs 다.

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

EnrollmentID 속성은 기본 키입니다. 이 엔터티는 자체적으로 ID 대신 classnameID 패턴을 사용합니다. 프로덕션 데이터 모델의 경우 하나의 패턴을 선택하고 일관되게 사용합니다. 이 자습서에서는 두 항목이 작동하는 것을 보여 주기 위해 둘 다 사용합니다. classname 없이 ID를 사용하면 특정 종류의 데이터 모델 변경 내용을 더 쉽게 구현할 수 있습니다.

Grade 속성은 enum입니다. Grade 형식 선언 뒤에 있는 물음표는 Grade 속성이 nullable이라는 것을 나타냅니다. Null인 등급은 0 등급과는 다릅니다. Null은 알려지지 않거나 아직 등록되지 않은 등급을 의미합니다.

StudentID 속성은 외래 키로, 해당 탐색 속성은 Student입니다. Enrollment 엔터티는 하나의 Student 엔터티와 연결되므로 속성은 단일 Student 엔터티를 포함합니다.

CourseID 속성은 외래 키로, 해당 탐색 속성은 Course입니다. Enrollment 엔터티는 하나의 Course 엔터티와 연결됩니다.

EF Core는 속성 이름이 <navigation property name><primary key property name>인 경우 외래 키로 해석합니다. 예를 들어 Student 엔터티의 기본 키는 ID이므로 StudentIDStudent 탐색 속성의 외래 키입니다. 외래 키 속성의 이름은 <primary key property name>으로 지정할 수 있습니다. 예를 들어 Course 엔터티의 기본 키가 CourseID이므로 CourseID라고 할 수 있습니다.

강좌 엔터티

Course entity diagram

다음 코드를 사용하여 만듭니 Models/Course.cs 다.

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

Enrollments 속성은 탐색 속성입니다. Course 엔터티는 Enrollment 엔터티의 개수와 관련이 있을 수 있습니다.

DatabaseGenerated 특성을 사용하면 데이터베이스에서 기본 키를 생성하도록 하지 않고 앱에서 기본 키를 지정할 수 있습니다.

프로젝트를 빌드하여 컴파일러 오류가 없는지 유효성을 검사합니다.

학생 페이지 스캐폴드

이 섹션에서는 ASP.NET Core 스캐폴딩 도구를 사용하여 다음을 생성합니다.

  • 컨텍스트 클래스입니다.EF Core 컨텍스트는 특정 데이터 모델에 맞게 Entity Framework 기능을 조정하는 주 클래스입니다. 이 클래스는 Microsoft.EntityFrameworkCore.DbContext 클래스에서 파생됩니다.
  • Student 엔터티에 대한 CRUD(만들기, 읽기, 업데이트 및 삭제) 작업을 처리하는 Razor 페이지.
  • Pages 폴더에 Students 폴더를 만듭니다.
  • 솔루션 탐색기에서 Pages/Students 폴더를 마우스 오른쪽 단추로 클릭하고 추가>새 스캐폴드된 항목을 선택합니다.
  • 스캐폴드 추가 대화 상자에서 Entity Framework를 사용한 Razor Pages(CRUD)>추가를 선택합니다.
  • CRUD(Entity Framework)를 사용하여 페이지 추가 Razor 대화 상자에서 다음을 수행합니다.
    • 모델 클래스 드롭다운에서 학생(ContosoUniversity.Models)을 선택합니다.
    • 데이터 컨텍스트 클래스 행에서 +(더하기) 기호를 선택합니다.
    • 데이터 컨텍스트 이름을 ContosoUniversity.Models.ContosoUniversityContext에서 ContosoUniversity.Data.SchoolContext로 변경합니다.
    • 추가를 선택합니다.

다음 패키지가 자동으로 설치됩니다.

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

이전 단계에서 문제가 발생하면 프로젝트를 빌드하고 스캐폴드 단계를 다시 시도합니다.

스캐폴딩 프로세스:

  • Pages/Students 폴더에 페이지를 만듭니다Razor.
    • Create.cshtmlCreate.cshtml.cs
    • Delete.cshtmlDelete.cshtml.cs
    • Details.cshtmlDetails.cshtml.cs
    • Edit.cshtmlEdit.cshtml.cs
    • Index.cshtmlIndex.cshtml.cs
  • Data/SchoolContext.cs를 만듭니다.
  • 에서 종속성 주입에 컨텍스트를 Startup.cs추가합니다.
  • appsettings.json에 데이터베이스 연결 문자열을 추가합니다.

데이터베이스 연결 문자열

appsettings.json 파일은 연결 문자열 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는 프로덕션 사용이 아닌 앱 개발용으로 고안된 SQL Server Express 데이터베이스 엔진의 경량 버전입니다. 기본적으로 LocalDB는 C:/Users/<user> 디렉터리에 .mdf 파일을 만듭니다.

데이터베이스 컨텍스트 클래스 업데이트

지정된 데이터 모델의 EF Core 기능을 조정하는 주 클래스는 데이터베이스 컨텍스트 클래스입니다. 컨텍스트는 Microsoft.EntityFrameworkCore.DbContext에서 파생됩니다. 컨텍스트는 데이터 모델에 포함되는 엔터티를 지정합니다. 이 프로젝트에서 클래스 이름은 SchoolContext로 지정됩니다.

다음 코드를 사용하여 Data/SchoolContext.cs을 업데이트합니다.

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

강조 표시된 코드는 각 엔터티 집합에 대해 DbSet<TEntity> 속성을 생성합니다. EF Core 용어:

  • 엔터티 집합은 일반적으로 데이터베이스 테이블에 해당합니다.
  • 엔터티는 테이블의 행에 해당합니다.

엔터티 집합은 여러 엔터티를 포함하므로 DBSet 속성은 복수형 이름이어야 합니다. 스캐폴딩 도구로 Student DBSet를 만들었으므로 이 단계에서는 이 DBSet를 복수형 Students로 변경합니다.

Razor Pages 코드가 새 DBSet 이름과 일치하게 만들려면 전체 프로젝트에서 _context.Student_context.Students로 전체 변경합니다. 8개 발생 항목이 있습니다.

프로젝트를 빌드하여 컴파일러 오류가 없는지 확인합니다.

Startup.cs

ASP.NET Core는 종속성 주입을 사용하여 빌드됩니다. 서비스(예: EF Core 데이터베이스 컨텍스트)는 애플리케이션 시작 중에 종속성 주입에 등록됩니다. 이러한 서비스(예: Razor Pages)가 필요한 구성 요소는 생성자 매개 변수를 통해 해당 서비스를 제공받습니다. 데이터베이스 컨텍스트 인스턴스를 가져오는 생성자 코드는 자습서 뒷부분에 나옵니다.

스캐폴딩 도구는 종속성 주입 컨테이너에 컨텍스트 클래스를 자동으로 등록합니다.

  • ConfigureServices에서 강조 표시된 줄은 스캐폴더에서 추가되었습니다.

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

DbContextOptions 개체에서 메서드를 호출하여 연결 문자열의 이름을 컨텍스트에 전달합니다. 로컬 개발의 경우 ASP.NET Core 구성 시스템appsettings.json 파일에서 연결 문자열을 읽습니다.

데이터베이스 생성

데이터베이스가 없는 경우 데이터베이스를 만들도록 업데이트 Program.cs 합니다.

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

EnsureCreated 컨텍스트에 대한 데이터베이스가 있는 경우 메서드는 아무 작업도 수행하지 않습니다. 데이터베이스가 없는 경우 데이터베이스 및 스키마를 만듭니다. EnsureCreated를 사용하면 다음 워크플로가 데이터 모델 변경 내용을 처리할 수 있습니다.

  • 데이터베이스를 삭제합니다. 모든 기존 데이터가 손실됩니다.
  • 데이터 모델을 변경합니다. 예를 들어 EmailAddress 필드를 추가합니다.
  • 앱을 실행합니다.
  • EnsureCreated는 새 스키마를 사용하여 데이터베이스를 만듭니다.

이 워크플로는 데이터를 보존할 필요가 없는 경우 스키마가 빠르게 업데이트되는 개발 초기에 적합합니다. 데이터베이스에 입력된 데이터를 보존해야 하는 경우는 상황이 다릅니다. 이 경우에는 마이그레이션을 사용합니다.

자습서 시리즈의 뒷부분에서는 EnsureCreated에서 만든 데이터베이스를 삭제하고 마이그레이션을 대신 사용합니다. EnsureCreated에서 만든 데이터베이스는 마이그레이션을 사용하여 업데이트할 수 없습니다.

앱 테스트

  • 앱을 실행합니다.
  • 학생 링크 및 새로 만들기를 차례로 선택합니다.
  • Edit, Details 및 Delete 링크를 테스트합니다.

데이터베이스 시드

EnsureCreated 메서드는 빈 데이터베이스를 만듭니다. 이 섹션에서는 테스트 데이터로 데이터베이스를 채우는 코드를 추가합니다.

다음 코드를 사용하여 만듭니 Data/DbInitializer.cs 다.

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

이 코드는 데이터베이스에 학생이 있는지 확인합니다. 학생이 없는 경우 테스트 데이터를 데이터베이스에 추가합니다. List<T> 컬렉션이 아닌 배열에 테스트 데이터를 만들어 성능을 최적화합니다.

  • 에서 Program.cs호출을 EnsureCreated 호출로 바꿉다.DbInitializer.Initialize

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

앱이 실행 중이라면 중지하고 패키지 관리자 콘솔(PMC)에서 다음 명령을 실행합니다.

Drop-Database
  • 앱을 다시 시작합니다.

  • 학생 페이지를 선택하여 시드된 데이터를 확인합니다.

데이터베이스 보기

  • Visual Studio의 보기 메뉴에서 SSOX(SQL Server 개체 탐색기)를 엽니다.
  • SSOX에서 (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}를 선택합니다. 데이터베이스 이름은 이전에 제공한 컨텍스트 이름과 대시 및 GUID를 사용하여 생성됩니다.
  • 테이블 노드를 확장합니다.
  • 학생 테이블을 마우스 오른쪽 단추로 클릭하고, 데이터 보기를 클릭하여 만든 열 및 테이블에 삽입된 행을 볼 수 있습니다.
  • Student 테이블을 마우스 오른쪽 단추로 클릭하고 코드 보기를 클릭하여 Student 모델이 Student 테이블 스키마에 어떻게 매핑되는지 확인합니다.

비동기 코드

비동기 프로그래밍은 ASP.NET Core 및 EF Core에 대한 기본 모드입니다.

웹 서버에는 사용할 수 있는 스레드 수가 제한적이며, 로드 양이 많은 상황에서는 사용 가능한 모든 스레드가 사용될 수 있습니다. 이 경우 서버는 스레드가 해제될 때까지 새 요청을 처리할 수 없습니다. 동기 코드를 사용하면 I/O 완료를 대기하느라 작업을 실제로 수행하지 않는 동안에 많은 스레드가 정체될 수 있습니다. 비동기 코드를 사용하면 프로세스가 I/O 완료를 대기할 때 다른 요청을 처리하는 데 사용하도록 해당 스레드가 서버에서 해제됩니다. 따라서 비동기 코드를 사용하면 서버 리소스를 더 효율적으로 사용할 수 있으며 서버가 지연 없이 더 많은 트래픽을 처리할 수 있습니다.

비동기 코드는 런타임 시 약간의 오버헤드를 도입합니다. 트래픽이 높은 상황에서는 잠재적 성능 향상이 상당한 반면, 트래픽이 낮은 상황의 경우 성능 저하는 미미합니다.

다음 코드에서 async 키워드, Task<T> 반환 값, await 키워드 및 ToListAsync 메서드는 비동기적으로 코드 실행을 수행합니다.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • async 키워드(keyword) 컴파일러에 다음을 지시합니다.
    • 메서드 본문의 부분에 대한 콜백을 생성합니다.
    • 반환되는 Task 개체를 만듭니다.
  • Task<T> 반환 형식은 진행 중인 작업을 나타냅니다.
  • await 키워드로 인해 컴파일러는 메서드를 두 부분으로 분할합니다. 첫 번째 부분은 비동기적으로 시작되는 작업을 종료합니다. 두 번째 부분은 작업이 완료될 때 호출되는 콜백 메서드에 배치됩니다.
  • ToListAsyncToList 확장 메서드의 비동기 버전입니다.

EF Core를 사용하는 비동기 코드를 작성할 때 주의 사항:

  • 쿼리 또는 명령을 데이터베이스에 보내는 명령문만 비동기적으로 실행됩니다. 여기에는 ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsyncSaveChangesAsync가 포함됩니다. var students = context.Students.Where(s => s.LastName == "Davolio")와 같은 IQueryable을 변경하는 명령문은 포함되지 않습니다.
  • EF Core 컨텍스트는 스레드로부터 안전하지 않습니다. 동시에 여러 작업을 수행하지 마세요.
  • 비동기 코드의 성능 이점을 이용하려면 라이브러리 패키지(예: 페이징)가 쿼리를 데이터베이스에 보내는 EF Core 메서드를 호출하는 경우 비동기를 사용하는지 확인합니다.

.NET에서의 비동기 프로그래밍에 대한 자세한 내용은 비동기 개요Async 및 Await를 사용한 비동기 프로그래밍을 참조하세요.

다음 단계