4부. ASP.NET Core MVC 앱에 모델 추가

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

Important

이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.

현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

작성자: Rick AndersonJon P Smith.

이 자습서에서는 데이터베이스에서 동영상을 관리하기 위한 클래스를 추가합니다. 이러한 클래스는 MVC 앱의 "Model" 부분입니다.

이러한 모델 클래스를 EF Core(Entity Framework Core)(EF Core)와 함께 사용하여 데이터베이스 작업을 수행합니다. EF Core는 작성해야 할 데이터 액세스 코드를 간소화하는 ORM(개체-관계형 매핑) 프레임워크입니다.

만들어진 모델 클래스는 POCO(Plain Old CLR Objects) 클래스라고 합니다. POCO 클래스는 EF Core에 대한 종속성이 없습니다. 이 클래스는 데이터베이스에 저장되는 데이터의 속성만 정의합니다.

이 자습서에서는 먼저 모델 클래스가 작성되며, EF Core가 데이터베이스를 만듭니다.

데이터 모델 클래스 추가

Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가>클래스를 선택합니다. 파일 이름을 Movie.cs로 지정합니다.

Models/Movie.cs 파일을 다음 코드로 업데이트합니다.

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Movie 클래스에는 데이터베이스에서 기본 키에 필요한 Id 필드가 포함되어 있습니다.

ReleaseDateDataType 특성은 날짜 형식(Date)을 지정합니다. 이 특성을 사용하면:

  • 사용자가 날짜 필드에 시간 정보를 입력할 필요가 없습니다.
  • 시간 정보 없이 날짜만 표시됩니다.

DataAnnotations는 이후 자습서에서 다룹니다.

string 다음의 물음표는 속성이 null 허용임을 나타냅니다. 자세한 내용은 nullable 참조 형식을 참조하세요.

NuGet 패키지 추가

Visual Studio는 필요한 패키지를 자동으로 설치합니다.

컴파일러 오류에 대한 검사로 프로젝트를 빌드합니다.

영화 페이지 스캐폴드

스캐폴딩 도구를 사용하여 영화 모델에 대한 CRUD(Create, Read, UpdateDelete) 페이지를 생성합니다.

솔루션 탐색기에서 Controllers 폴더를 마우스 오른쪽 단추로 클릭하고 추가> 스캐폴드 항목 새로 만들기를 선택합니다.

위 단계의 보기

새 스캐폴드된 항목 추가 대화 상자에서 다음을 수행합니다.

  • 왼쪽 창에서 설치된 공용>MVC>선택합니다.
  • Entity Framework를 사용하여 보기가 있는 MVC 컨트롤러를 선택합니다.
  • 추가를 선택합니다.

스캐폴드 추가 대화 상자

다음과 같이 Entity Framework를 사용하여 뷰 포함 MVC 컨트롤러 추가 대화 상자의 작업을 완료합니다.

  • 모델 클래스 드롭다운에서 동영상(MvcMovie.Models)을 선택합니다.
  • 데이터 컨텍스트 클래스 행에서 +(더하기) 기호를 선택합니다.
    • 데이터 컨텍스트 추가 대화 상자에서 클래스 이름 MvcMovie.Data.MvcMovieContext가 생성됩니다.
    • 추가를 선택합니다.
  • 데이터베이스 공급자 드롭다운에서 SQL Server를 선택합니다.
  • 컨트롤러 이름: 기본값을 유지합니다.
  • 추가를 선택합니다.

데이터 컨텍스트 추가 기본값 유지

오류 메시지가 표시되면 추가를 선택하여 다시 시도합니다.

스캐폴딩은 다음 패키지를 추가합니다.

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

스캐폴딩은 다음을 만듭니다.

  • 영화 컨트롤러: Controllers/MoviesController.cs
  • Create, Delete, Details, EditIndex 페이지에 대한 Razor 뷰 파일: Views/Movies/*.cshtml
  • 데이터베이스 컨텍스트 클래스 추가: Data/MvcMovieContext.cs

스캐폴딩은 다음과 같이 업데이트를 수행합니다.

  • 프로젝트 파일에 필요한 패키지 참조를 MvcMovie.csproj 삽입합니다.
  • 파일에 데이터베이스 컨텍스트를 등록합니다 Program.cs .
  • appsettings.json 파일에 데이터베이스 연결 문자열을 추가합니다.

이러한 파일 및 파일 업데이트의 자동 생성을 스캐폴딩이라고 합니다.

데이터베이스가 없으므로 아직 스캐폴드된 페이지를 사용할 수 없습니다. 앱을 실행하고 동영상 영화 앱 링크를 선택하면 데이터베이스를 열 수 없음 또는 해당 테이블이 없음: Movie 오류 메시지가 표시됩니다.

앱을 빌드하여 오류가 없는지 확인합니다.

초기 마이그레이션

EF Core마이그레이션 기능을 사용하여 데이터베이스를 만듭니다. 마이그레이션은 데이터 모델과 일치하도록 데이터베이스를 만들고 수정할 수 있는 도구 모음입니다.

도구 메뉴에서 NuGet 패키지 관리자>패키지 관리자 콘솔을 선택합니다.

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

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: 마이그레이션 파일을 생성합니다 Migrations/{timestamp}_InitialCreate.cs . InitialCreate 인수는 마이그레이션 이름입니다. 모든 이름을 사용할 수 있지만 규칙에 따라 마이그레이션을 설명하는 이름을 선택합니다. 이는 첫 번째 마이그레이션이므로 생성된 클래스에는 데이터베이스 스키마를 만드는 코드가 포함되어 있습니다. 데이터베이스 스키마는 MvcMovieContext 클래스에 지정된 모델을 기반으로 합니다.

  • Update-Database: 데이터베이스를 이전 명령이 만든 최신 마이그레이션으로 업데이트. 이 명령은 파일에서 Up 메서드를 Migrations/{time-stamp}_InitialCreate.cs 실행하여 데이터베이스를 만듭니다.

Update-Database 명령은 다음과 같은 경고를 생성합니다.

엔터티 형식 'Movie'의 10진수 속성 'Price'에 대해 저장소 형식이 지정되지 않았습니다. 그러면 값이 기본 전체 자릿수 및 소수 자릿수에 적합하지 않은 경우 자동으로 잘립니다. 'HasColumnType'을 사용하여 'OnModelCreating'의 모든 값을 수용할 수 있는 SQL Server 열 형식을 명시적으로 지정하거나, 'HasPrecision'을 사용하여 정밀도 및 배율을 지정하거나, 'HasConversion'을 사용하여 값 변환기를 구성합니다.

위의 경고를 무시하세요. 이 경고는 이후 자습서에서 수정되었습니다.

EF Core의 PMC 도구에 대한 자세한 내용은 EF Core 도구 참조 - Visual Studio의 PMC를 참조하세요.

앱 테스트

앱을 실행하고 영화 앱 링크를 클릭합니다.

다음과 유사한 예외가 발생하면 마이그레이션 단계에서 Update-Database 명령을 누락했을 수 있습니다.

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

참고 항목

Price 필드에 소수점을 입력하지 못할 수도 있습니다. 소수점으로 쉼표(",")를 사용하는 비 영어 로캘 및 비 US-English 날짜 형식에 대해jQuery 유효성 검사를 지원하려면 앱을 세계화해야 합니다. 세계화 지침은 이 GitHub 문제를 참조하세요.

생성된 데이터베이스 컨텍스트 클래스 및 등록 검사

EF Core에서는 데이터 액세스가 모델을 통해 수행됩니다. 모델은 엔터티 클래스 및 데이터베이스와의 세션을 나타내는 컨텍스트 개체로 구성됩니다. 컨텍스트 개체를 사용하여 데이터를 쿼리하고 저장할 수 있습니다. 데이터베이스 컨텍스트는 Microsoft.EntityFrameworkCore.DbContext에서 파생되며 데이터 모델에 포함할 엔터티를 지정합니다.

스캐폴딩은 데이터베이스 컨텍스트 클래스를 Data/MvcMovieContext.cs 만듭니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

위의 코드는 데이터베이스의 영화를 나타내는 DbSet<Movie> 속성을 만듭니다.

종속성 주입

ASP.NET Core는 DI(종속성 주입)를 사용하여 만들어집니다. 데이터베이스 컨텍스트와 같은 서비스는 에서 DI Program.cs에 등록됩니다. 이러한 서비스가 필요한 구성 요소는 생성자 매개 변수를 통해 해당 서비스를 제공받습니다.

Controllers/MoviesController.cs 파일에서 생성자는 종속성 주입을 사용하여 MvcMovieContext 컨트롤러에 데이터베이스 컨텍스트를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

스캐폴딩은 Program.cs에서 다음과 같이 강조 표시된 코드를 생성했습니다.

var builder = WebApplication.CreateBuilder(args);

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

ASP.NET Core 구성 시스템은 "MvcMovieContext" 데이터베이스 연결 문자열을 읽습니다.

생성된 데이터베이스 연결 문자열 검사

스캐폴딩은 appsettings.json 파일에 연결 문자열을 추가했습니다.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

로컬 개발의 경우 ASP.NET Core 구성 시스템appsettings.json 파일에서 ConnectionString 키를 읽습니다.

InitialCreate 클래스

마이그레이션 파일을 검사합니다.Migrations/{timestamp}_InitialCreate.cs

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

위의 코드에서

  • InitialCreate.Up은 Movie 테이블을 만들고 Id를 기본 키로 구성합니다.
  • InitialCreate.DownUp 마이그레이션에 의해 변경된 스키마를 되돌립니다.

컨트롤러에서 종속성 주입

Controllers/MoviesController.cs 파일을 열고 생성자를 검사합니다.

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

생성자는 종속성 주입을 사용하여 컨트롤러에 데이터베이스 컨텍스트(MvcMovieContext)를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

Create 페이지를 테스트합니다. 데이터를 입력하고 제출합니다.

Edit, DetailsDelete 링크를 테스트합니다.

강력한 형식의 모델 및 @model 지시문

이 자습서의 앞부분에서는 컨트롤러가 ViewData 사전을 사용하여 데이터 또는 개체를 보기에 전달하는 방법을 살펴봤습니다. ViewData 사전은 정보를 보기에 전달하기 위한 편리한 런타임 바인딩 방법을 제공하는 동적 개체입니다.

MVC는 보기에 강력한 형식의 모델 개체를 전달하는 기능도 제공합니다. 강력한 형식의 방법을 사용하면 컴파일 시에 코드 검사를 수행할 수 있습니다. 스캐폴딩 메커니즘은 MoviesController 클래스 및 뷰에서 강력한 형식의 모델을 전달했습니다.

Controllers/MoviesController.cs 파일에서 생성된 Details 메서드를 검사합니다.

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

일반적으로 id 매개 변수는 경로 데이터 변수로 전달됩니다. 예를 들어 https://localhost:5001/movies/details/1은 다음을 설정합니다.

  • 컨트롤러를 movies 컨트롤러로 설정(첫 번째 URL 세그먼트)
  • 작업을 details로 설정(두 번째 URL 세그먼트)
  • id를 1로 설정(마지막 URL 세그먼트)

id는 다음 예제와 같이 쿼리 문자열을 통해 전달할 수 있습니다.

https://localhost:5001/movies/details?id=1

id 값이 제공되지 않는 경우에 id 매개 변수가 nullable 형식(int?)으로 정의됩니다.

람다 식FirstOrDefaultAsync 메서드에 전달하여 경로 데이터 또는 쿼리 문자열 값과 일치하는 영화 엔터티를 선택합니다.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

영화를 찾으면 Movie 모델의 인스턴스를 Details 보기에 전달합니다.

return View(movie);

Views/Movies/Details.cshtml 파일의 콘텐츠를 검사합니다.

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

보기 파일 맨 위에 있는 @model 문은 보기에 필요한 개체 형식을 지정합니다. 영화 컨트롤러가 만들어질 때 다음 @model 문이 포함됩니다.

@model MvcMovie.Models.Movie

@model 지시문을 사용하면 컨트롤러가 보기에 전달한 영화에 액세스할 수 있습니다. Model 개체는 강력한 형식입니다. 예를 들어 Details.cshtml 보기에서 코드는 강력한 형식의 Model 개체를 사용하여 DisplayNameForDisplayFor HTML 도우미에 각 영화 필드를 전달합니다. 또한 CreateEdit 메서드와 보기도 Movie 모델 개체를 전달합니다.

Index.cshtml 보기 및 영화 컨트롤러의 Index 메서드를 확인합니다. 코드가 View 메서드를 호출할 때 List 개체를 생성하는 방법에 유의하세요. 이 코드는 Index 작업 메서드에서 보기로 Movies 목록을 전달합니다.

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

데이터 컨텍스트의 Movie 속성이 null인 경우 코드는 문제 세부 정보를 반환합니다.

영화 컨트롤러를 만들 때 스캐폴딩은 파일 맨 위에 다음 @model 문을 포함했습니다 Index.cshtml .

@model IEnumerable<MvcMovie.Models.Movie>

@model 지시문을 사용하면 강력한 형식인 Model 개체를 사용하여 컨트롤러가 보기에 전달한 영화 목록에 액세스할 수 있습니다. 예를 들어 Index.cshtml 보기에서 코드는 강력한 형식의 Model 개체에 대해 foreach 문을 사용하여 영화를 반복합니다.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

Model 개체가 IEnumerable<Movie> 개체로서 강력한 형식이 지정되었으므로 루프의 각 항목은 Movie 형식입니다. 여러 가지 이점이 있지만 컴파일러는 코드에 사용된 형식이 유효한지도 검사합니다.

추가 리소스

이 자습서에서는 데이터베이스에서 동영상을 관리하기 위한 클래스를 추가합니다. 이러한 클래스는 MVC 앱의 "Model" 부분입니다.

이러한 모델 클래스를 EF Core(Entity Framework Core)(EF Core)와 함께 사용하여 데이터베이스 작업을 수행합니다. EF Core는 작성해야 할 데이터 액세스 코드를 간소화하는 ORM(개체-관계형 매핑) 프레임워크입니다.

만들어진 모델 클래스는 POCO(Plain Old CLR Objects) 클래스라고 합니다. POCO 클래스는 EF Core에 대한 종속성이 없습니다. 이 클래스는 데이터베이스에 저장되는 데이터의 속성만 정의합니다.

이 자습서에서는 먼저 모델 클래스가 작성되며, EF Core가 데이터베이스를 만듭니다.

데이터 모델 클래스 추가

Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가>클래스를 선택합니다. 파일 이름을 Movie.cs로 지정합니다.

Models/Movie.cs 파일을 다음 코드로 업데이트합니다.

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Movie 클래스에는 데이터베이스에서 기본 키에 필요한 Id 필드가 포함되어 있습니다.

ReleaseDateDataType 특성은 날짜 형식(Date)을 지정합니다. 이 특성을 사용하면:

  • 사용자가 날짜 필드에 시간 정보를 입력할 필요가 없습니다.
  • 시간 정보 없이 날짜만 표시됩니다.

DataAnnotations는 이후 자습서에서 다룹니다.

string 다음의 물음표는 속성이 null 허용임을 나타냅니다. 자세한 내용은 nullable 참조 형식을 참조하세요.

NuGet 패키지 추가

Visual Studio는 필요한 패키지를 자동으로 설치합니다.

컴파일러 오류에 대한 검사로 프로젝트를 빌드합니다.

영화 페이지 스캐폴드

스캐폴딩 도구를 사용하여 영화 모델에 대한 CRUD(Create, Read, UpdateDelete) 페이지를 생성합니다.

솔루션 탐색기에서 Controllers 폴더를 마우스 오른쪽 단추로 클릭하고 추가> 스캐폴드 항목 새로 만들기를 선택합니다.

위 단계의 보기

새 스캐폴드된 항목 추가 대화 상자에서 다음을 수행합니다.

  • 왼쪽 창에서 설치된 공용>MVC>선택합니다.
  • Entity Framework를 사용하여 보기가 있는 MVC 컨트롤러를 선택합니다.
  • 추가를 선택합니다.

스캐폴드 추가 대화 상자

다음과 같이 Entity Framework를 사용하여 뷰 포함 MVC 컨트롤러 추가 대화 상자의 작업을 완료합니다.

  • 모델 클래스 드롭다운에서 동영상(MvcMovie.Models)을 선택합니다.
  • 데이터 컨텍스트 클래스 행에서 +(더하기) 기호를 선택합니다.
    • 데이터 컨텍스트 추가 대화 상자에서 클래스 이름 MvcMovie.Data.MvcMovieContext가 생성됩니다.
    • 추가를 선택합니다.
  • 데이터베이스 공급자 드롭다운에서 SQL Server를 선택합니다.
  • 컨트롤러 이름: 기본값을 유지합니다.
  • 추가를 선택합니다.

데이터 컨텍스트 추가 기본값 유지오류 메시지가 표시되면 두 번째 추가를 선택하여 다시 시도합니다.

스캐폴딩은 다음 패키지를 추가합니다.

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

스캐폴딩은 다음을 만듭니다.

  • 영화 컨트롤러: Controllers/MoviesController.cs
  • Create, Delete, Details, EditIndex 페이지에 대한 Razor 뷰 파일: Views/Movies/*.cshtml
  • 데이터베이스 컨텍스트 클래스 추가: Data/MvcMovieContext.cs

스캐폴딩은 다음과 같이 업데이트를 수행합니다.

  • 프로젝트 파일에 필요한 패키지 참조를 MvcMovie.csproj 삽입합니다.
  • 파일에 데이터베이스 컨텍스트를 등록합니다 Program.cs .
  • appsettings.json 파일에 데이터베이스 연결 문자열을 추가합니다.

이러한 파일 및 파일 업데이트의 자동 생성을 스캐폴딩이라고 합니다.

데이터베이스가 없으므로 아직 스캐폴드된 페이지를 사용할 수 없습니다. 앱을 실행하고 동영상 영화 앱 링크를 선택하면 데이터베이스를 열 수 없음 또는 해당 테이블이 없음: Movie 오류 메시지가 표시됩니다.

앱을 빌드하여 오류가 없는지 확인합니다.

초기 마이그레이션

EF Core마이그레이션 기능을 사용하여 데이터베이스를 만듭니다. 마이그레이션은 데이터 모델과 일치하도록 데이터베이스를 만들고 수정할 수 있는 도구 모음입니다.

도구 메뉴에서 NuGet 패키지 관리자>패키지 관리자 콘솔을 선택합니다.

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

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: 마이그레이션 파일을 생성합니다 Migrations/{timestamp}_InitialCreate.cs . InitialCreate 인수는 마이그레이션 이름입니다. 모든 이름을 사용할 수 있지만 규칙에 따라 마이그레이션을 설명하는 이름을 선택합니다. 이는 첫 번째 마이그레이션이므로 생성된 클래스에는 데이터베이스 스키마를 만드는 코드가 포함되어 있습니다. 데이터베이스 스키마는 MvcMovieContext 클래스에 지정된 모델을 기반으로 합니다.

  • Update-Database: 데이터베이스를 이전 명령이 만든 최신 마이그레이션으로 업데이트. 이 명령은 파일에서 Up 메서드를 Migrations/{time-stamp}_InitialCreate.cs 실행하여 데이터베이스를 만듭니다.

Update-Database 명령은 다음과 같은 경고를 생성합니다.

No type was specified for the decimal column ‘Price’ on entity type ‘Movie’. 그러면 값이 기본 전체 자릿수 및 소수 자릿수에 적합하지 않은 경우 자동으로 잘립니다. Explicitly specify the SQL server column type that can accommodate all the values using ‘HasColumnType()’.

위의 경고를 무시하세요. 이 경고는 이후 자습서에서 수정되었습니다.

EF Core의 PMC 도구에 대한 자세한 내용은 EF Core 도구 참조 - Visual Studio의 PMC를 참조하세요.

앱 테스트

앱을 실행하고 영화 앱 링크를 클릭합니다.

다음과 유사한 예외가 발생하면 마이그레이션 단계에서 Update-Database 명령을 누락했을 수 있습니다.

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

참고 항목

Price 필드에 소수점을 입력하지 못할 수도 있습니다. 소수점으로 쉼표(",")를 사용하는 비 영어 로캘 및 비 US-English 날짜 형식에 대해jQuery 유효성 검사를 지원하려면 앱을 세계화해야 합니다. 세계화 지침은 이 GitHub 문제를 참조하세요.

생성된 데이터베이스 컨텍스트 클래스 및 등록 검사

EF Core에서는 데이터 액세스가 모델을 통해 수행됩니다. 모델은 엔터티 클래스 및 데이터베이스와의 세션을 나타내는 컨텍스트 개체로 구성됩니다. 컨텍스트 개체를 사용하여 데이터를 쿼리하고 저장할 수 있습니다. 데이터베이스 컨텍스트는 Microsoft.EntityFrameworkCore.DbContext에서 파생되며 데이터 모델에 포함할 엔터티를 지정합니다.

스캐폴딩은 데이터베이스 컨텍스트 클래스를 Data/MvcMovieContext.cs 만듭니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

위의 코드는 데이터베이스의 영화를 나타내는 DbSet<Movie> 속성을 만듭니다.

종속성 주입

ASP.NET Core는 DI(종속성 주입)를 사용하여 만들어집니다. 데이터베이스 컨텍스트와 같은 서비스는 에서 DI Program.cs에 등록됩니다. 이러한 서비스가 필요한 구성 요소는 생성자 매개 변수를 통해 해당 서비스를 제공받습니다.

Controllers/MoviesController.cs 파일에서 생성자는 종속성 주입을 사용하여 MvcMovieContext 컨트롤러에 데이터베이스 컨텍스트를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

스캐폴딩은 Program.cs에서 다음과 같이 강조 표시된 코드를 생성했습니다.

var builder = WebApplication.CreateBuilder(args);

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

ASP.NET Core 구성 시스템은 "MvcMovieContext" 데이터베이스 연결 문자열을 읽습니다.

생성된 데이터베이스 연결 문자열 검사

스캐폴딩은 appsettings.json 파일에 연결 문자열을 추가했습니다.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

로컬 개발의 경우 ASP.NET Core 구성 시스템appsettings.json 파일에서 ConnectionString 키를 읽습니다.

InitialCreate 클래스

마이그레이션 파일을 검사합니다.Migrations/{timestamp}_InitialCreate.cs

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

위의 코드에서

  • InitialCreate.Up은 Movie 테이블을 만들고 Id를 기본 키로 구성합니다.
  • InitialCreate.DownUp 마이그레이션에 의해 변경된 스키마를 되돌립니다.

컨트롤러에서 종속성 주입

Controllers/MoviesController.cs 파일을 열고 생성자를 검사합니다.

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

생성자는 종속성 주입을 사용하여 컨트롤러에 데이터베이스 컨텍스트(MvcMovieContext)를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

Create 페이지를 테스트합니다. 데이터를 입력하고 제출합니다.

Edit, DetailsDelete 링크를 테스트합니다.

강력한 형식의 모델 및 @model 지시문

이 자습서의 앞부분에서는 컨트롤러가 ViewData 사전을 사용하여 데이터 또는 개체를 보기에 전달하는 방법을 살펴봤습니다. ViewData 사전은 정보를 보기에 전달하기 위한 편리한 런타임 바인딩 방법을 제공하는 동적 개체입니다.

MVC는 보기에 강력한 형식의 모델 개체를 전달하는 기능도 제공합니다. 강력한 형식의 방법을 사용하면 컴파일 시에 코드 검사를 수행할 수 있습니다. 스캐폴딩 메커니즘은 MoviesController 클래스 및 뷰에서 강력한 형식의 모델을 전달했습니다.

Controllers/MoviesController.cs 파일에서 생성된 Details 메서드를 검사합니다.

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

일반적으로 id 매개 변수는 경로 데이터 변수로 전달됩니다. 예를 들어 https://localhost:5001/movies/details/1은 다음을 설정합니다.

  • 컨트롤러를 movies 컨트롤러로 설정(첫 번째 URL 세그먼트)
  • 작업을 details로 설정(두 번째 URL 세그먼트)
  • id를 1로 설정(마지막 URL 세그먼트)

id는 다음 예제와 같이 쿼리 문자열을 통해 전달할 수 있습니다.

https://localhost:5001/movies/details?id=1

id 값이 제공되지 않는 경우에 id 매개 변수가 nullable 형식(int?)으로 정의됩니다.

람다 식FirstOrDefaultAsync 메서드에 전달하여 경로 데이터 또는 쿼리 문자열 값과 일치하는 영화 엔터티를 선택합니다.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

영화를 찾으면 Movie 모델의 인스턴스를 Details 보기에 전달합니다.

return View(movie);

Views/Movies/Details.cshtml 파일의 콘텐츠를 검사합니다.

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

보기 파일 맨 위에 있는 @model 문은 보기에 필요한 개체 형식을 지정합니다. 영화 컨트롤러가 만들어질 때 다음 @model 문이 포함됩니다.

@model MvcMovie.Models.Movie

@model 지시문을 사용하면 컨트롤러가 보기에 전달한 영화에 액세스할 수 있습니다. Model 개체는 강력한 형식입니다. 예를 들어 Details.cshtml 보기에서 코드는 강력한 형식의 Model 개체를 사용하여 DisplayNameForDisplayFor HTML 도우미에 각 영화 필드를 전달합니다. 또한 CreateEdit 메서드와 보기도 Movie 모델 개체를 전달합니다.

Index.cshtml 보기 및 영화 컨트롤러의 Index 메서드를 확인합니다. 코드가 View 메서드를 호출할 때 List 개체를 생성하는 방법에 유의하세요. 이 코드는 Index 작업 메서드에서 보기로 Movies 목록을 전달합니다.

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

데이터 컨텍스트의 Movie 속성이 null인 경우 코드는 문제 세부 정보를 반환합니다.

영화 컨트롤러를 만들 때 스캐폴딩은 파일 맨 위에 다음 @model 문을 포함했습니다 Index.cshtml .

@model IEnumerable<MvcMovie.Models.Movie>

@model 지시문을 사용하면 강력한 형식인 Model 개체를 사용하여 컨트롤러가 보기에 전달한 영화 목록에 액세스할 수 있습니다. 예를 들어 Index.cshtml 보기에서 코드는 강력한 형식의 Model 개체에 대해 foreach 문을 사용하여 영화를 반복합니다.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

Model 개체가 IEnumerable<Movie> 개체로서 강력한 형식이 지정되었으므로 루프의 각 항목은 Movie 형식입니다. 여러 가지 이점이 있지만 컴파일러는 코드에 사용된 형식이 유효한지도 검사합니다.

추가 리소스

이 자습서에서는 데이터베이스에서 동영상을 관리하기 위한 클래스를 추가합니다. 이러한 클래스는 MVC 앱의 "Model" 부분입니다.

이러한 모델 클래스를 EF Core(Entity Framework Core)(EF Core)와 함께 사용하여 데이터베이스 작업을 수행합니다. EF Core는 작성해야 할 데이터 액세스 코드를 간소화하는 ORM(개체-관계형 매핑) 프레임워크입니다.

만들어진 모델 클래스는 POCO(Plain Old CLR Objects) 클래스라고 합니다. POCO 클래스는 EF Core에 대한 종속성이 없습니다. 이 클래스는 데이터베이스에 저장되는 데이터의 속성만 정의합니다.

이 자습서에서는 먼저 모델 클래스가 작성되며, EF Core가 데이터베이스를 만듭니다.

데이터 모델 클래스 추가

Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가>클래스를 선택합니다. 파일 이름을 Movie.cs로 지정합니다.

Models/Movie.cs 파일을 다음 코드로 업데이트합니다.

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string? Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string? Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie 클래스에는 데이터베이스에서 기본 키에 필요한 Id 필드가 포함되어 있습니다.

ReleaseDateDataType 특성은 날짜 형식(Date)을 지정합니다. 이 특성을 사용하면:

  • 사용자가 날짜 필드에 시간 정보를 입력할 필요가 없습니다.
  • 시간 정보 없이 날짜만 표시됩니다.

DataAnnotations는 이후 자습서에서 다룹니다.

string 다음의 물음표는 속성이 null 허용임을 나타냅니다. 자세한 내용은 nullable 참조 형식을 참조하세요.

NuGet 패키지 추가

도구 메뉴에서 NuGet 패키지 관리자> PMC(패키지 관리자 콘솔)를 선택합니다.

PMC 메뉴

PMC에서 다음 명령을 실행합니다.

Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer

앞의 명령은 다음을 추가합니다.

  • EF Core SQL Server 공급자. 이 공급자 패키지는 EF Core 패키지를 종속성으로 설치합니다.
  • 패키지에 사용되는 유틸리티(자습서 뒷부분의 스캐폴딩 단계에서 자동으로 설치됨).

컴파일러 오류에 대한 검사로 프로젝트를 빌드합니다.

영화 페이지 스캐폴드

스캐폴딩 도구를 사용하여 영화 모델에 대한 CRUD(Create, Read, UpdateDelete) 페이지를 생성합니다.

솔루션 탐색기에서 Controllers 폴더를 마우스 오른쪽 단추로 클릭하고 추가> 스캐폴드 항목 새로 만들기를 선택합니다.

위 단계의 보기

스캐폴드 추가 대화 상자에서 Entity Framework를 사용하며 뷰가 포함된 MVC 컨트롤러 > 추가를 선택합니다.

스캐폴드 추가 대화 상자

다음과 같이 Entity Framework를 사용하여 뷰 포함 MVC 컨트롤러 추가 대화 상자의 작업을 완료합니다.

  • 모델 클래스 드롭다운에서 동영상(MvcMovie.Models)을 선택합니다.
  • 데이터 컨텍스트 클래스 행에서 +(더하기) 기호를 선택합니다.
    • 데이터 컨텍스트 추가 대화 상자에서 클래스 이름 MvcMovie.Data.MvcMovieContext가 생성됩니다.
    • 추가를 선택합니다.
  • 컨트롤러 이름: 기본값을 유지합니다.
  • 추가를 선택합니다.

데이터 컨텍스트 추가 기본값 유지

오류 메시지가 표시되면 추가를 선택하여 다시 시도합니다.

스캐폴딩은 다음과 같이 업데이트를 수행합니다.

  • 프로젝트 파일에 필요한 패키지 참조를 MvcMovie.csproj 삽입합니다.
  • 파일에 데이터베이스 컨텍스트를 등록합니다 Program.cs .
  • appsettings.json 파일에 데이터베이스 연결 문자열을 추가합니다.

스캐폴딩은 다음을 만듭니다.

  • 영화 컨트롤러: Controllers/MoviesController.cs
  • Create, Delete, Details, EditIndex 페이지에 대한 Razor 뷰 파일: Views/Movies/*.cshtml
  • 데이터베이스 컨텍스트 클래스 추가: Data/MvcMovieContext.cs

이러한 파일 및 파일 업데이트의 자동 생성을 스캐폴딩이라고 합니다.

데이터베이스가 없으므로 아직 스캐폴드된 페이지를 사용할 수 없습니다. 앱을 실행하고 동영상 영화 앱 링크를 선택하면 데이터베이스를 열 수 없음 또는 해당 테이블이 없음: Movie 오류 메시지가 표시됩니다.

앱 빌드

앱을 빌드합니다. 컴파일러는 null 값이 처리되는 방법에 대한 몇 가지 경고를 생성합니다. 자세한 내용은 이 GitHub 문제nullable 참조 형식을 참조하세요.

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

<Nullable>enable</Nullable>

다음 릴리스에서 이 문제를 해결하기 위해 노력하고 있습니다.

초기 마이그레이션

EF Core마이그레이션 기능을 사용하여 데이터베이스를 만듭니다. 마이그레이션은 데이터 모델과 일치하도록 데이터베이스를 만들고 수정할 수 있는 도구 모음입니다.

도구 메뉴에서 NuGet 패키지 관리자>패키지 관리자 콘솔을 선택합니다.

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

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: 마이그레이션 파일을 생성합니다 Migrations/{timestamp}_InitialCreate.cs . InitialCreate 인수는 마이그레이션 이름입니다. 모든 이름을 사용할 수 있지만 규칙에 따라 마이그레이션을 설명하는 이름을 선택합니다. 이는 첫 번째 마이그레이션이므로 생성된 클래스에는 데이터베이스 스키마를 만드는 코드가 포함되어 있습니다. 데이터베이스 스키마는 MvcMovieContext 클래스에 지정된 모델을 기반으로 합니다.

  • Update-Database: 데이터베이스를 이전 명령이 만든 최신 마이그레이션으로 업데이트. 이 명령은 파일에서 Up 메서드를 Migrations/{time-stamp}_InitialCreate.cs 실행하여 데이터베이스를 만듭니다.

Update-Database 명령은 다음과 같은 경고를 생성합니다.

No type was specified for the decimal column ‘Price’ on entity type ‘Movie’. 그러면 값이 기본 전체 자릿수 및 소수 자릿수에 적합하지 않은 경우 자동으로 잘립니다. Explicitly specify the SQL server column type that can accommodate all the values using ‘HasColumnType()’.

위의 경고를 무시하세요. 이 경고는 이후 자습서에서 수정되었습니다.

EF Core의 PMC 도구에 대한 자세한 내용은 EF Core 도구 참조 - Visual Studio의 PMC를 참조하세요.

앱 테스트

앱을 실행하고 영화 앱 링크를 클릭합니다.

다음과 유사한 예외가 발생하면 마이그레이션 단계를 누락했을 수 있습니다.

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

참고 항목

Price 필드에 소수점을 입력하지 못할 수도 있습니다. 소수점으로 쉼표(",")를 사용하는 비 영어 로캘 및 비 US-English 날짜 형식에 대해jQuery 유효성 검사를 지원하려면 앱을 세계화해야 합니다. 세계화 지침은 이 GitHub 문제를 참조하세요.

생성된 데이터베이스 컨텍스트 클래스 및 등록 검사

EF Core에서는 데이터 액세스가 모델을 통해 수행됩니다. 모델은 엔터티 클래스 및 데이터베이스와의 세션을 나타내는 컨텍스트 개체로 구성됩니다. 컨텍스트 개체를 사용하여 데이터를 쿼리하고 저장할 수 있습니다. 데이터베이스 컨텍스트는 Microsoft.EntityFrameworkCore.DbContext에서 파생되며 데이터 모델에 포함할 엔터티를 지정합니다.

스캐폴딩은 데이터베이스 컨텍스트 클래스를 Data/MvcMovieContext.cs 만듭니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

위의 코드는 데이터베이스의 영화를 나타내는 DbSet<Movie> 속성을 만듭니다.

종속성 주입

ASP.NET Core는 DI(종속성 주입)를 사용하여 만들어집니다. 데이터베이스 컨텍스트와 같은 서비스는 에서 DI Program.cs에 등록됩니다. 이러한 서비스가 필요한 구성 요소는 생성자 매개 변수를 통해 해당 서비스를 제공받습니다.

Controllers/MoviesController.cs 파일에서 생성자는 종속성 주입을 사용하여 MvcMovieContext 컨트롤러에 데이터베이스 컨텍스트를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

스캐폴딩은 Program.cs에서 다음과 같이 강조 표시된 코드를 생성했습니다.

var builder = WebApplication.CreateBuilder(args);

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

ASP.NET Core 구성 시스템은 "MvcMovieContext" 데이터베이스 연결 문자열을 읽습니다.

생성된 데이터베이스 연결 문자열 검사

스캐폴딩은 appsettings.json 파일에 연결 문자열을 추가했습니다.

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

로컬 개발의 경우 ASP.NET Core 구성 시스템appsettings.json 파일에서 ConnectionString 키를 읽습니다.

InitialCreate 클래스

마이그레이션 파일을 검사합니다.Migrations/{timestamp}_InitialCreate.cs

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

위의 코드에서

  • InitialCreate.Up은 Movie 테이블을 만들고 Id를 기본 키로 구성합니다.
  • InitialCreate.DownUp 마이그레이션에 의해 변경된 스키마를 되돌립니다.

컨트롤러에서 종속성 주입

Controllers/MoviesController.cs 파일을 열고 생성자를 검사합니다.

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

생성자는 종속성 주입을 사용하여 컨트롤러에 데이터베이스 컨텍스트(MvcMovieContext)를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

Create 페이지를 테스트합니다. 데이터를 입력하고 제출합니다.

Edit, DetailsDelete 링크를 테스트합니다.

강력한 형식의 모델 및 @model 지시문

이 자습서의 앞부분에서는 컨트롤러가 ViewData 사전을 사용하여 데이터 또는 개체를 보기에 전달하는 방법을 살펴봤습니다. ViewData 사전은 정보를 보기에 전달하기 위한 편리한 런타임 바인딩 방법을 제공하는 동적 개체입니다.

MVC는 보기에 강력한 형식의 모델 개체를 전달하는 기능도 제공합니다. 강력한 형식의 방법을 사용하면 컴파일 시에 코드 검사를 수행할 수 있습니다. 스캐폴딩 메커니즘은 MoviesController 클래스 및 뷰에서 강력한 형식의 모델을 전달했습니다.

Controllers/MoviesController.cs 파일에서 생성된 Details 메서드를 검사합니다.

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

일반적으로 id 매개 변수는 경로 데이터 변수로 전달됩니다. 예를 들어 https://localhost:5001/movies/details/1은 다음을 설정합니다.

  • 컨트롤러를 movies 컨트롤러로 설정(첫 번째 URL 세그먼트)
  • 작업을 details로 설정(두 번째 URL 세그먼트)
  • id를 1로 설정(마지막 URL 세그먼트)

id는 다음 예제와 같이 쿼리 문자열을 통해 전달할 수 있습니다.

https://localhost:5001/movies/details?id=1

id 값이 제공되지 않는 경우에 id 매개 변수가 nullable 형식(int?)으로 정의됩니다.

람다 식FirstOrDefaultAsync 메서드에 전달하여 경로 데이터 또는 쿼리 문자열 값과 일치하는 영화 엔터티를 선택합니다.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

영화를 찾으면 Movie 모델의 인스턴스를 Details 보기에 전달합니다.

return View(movie);

Views/Movies/Details.cshtml 파일의 콘텐츠를 검사합니다.

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

보기 파일 맨 위에 있는 @model 문은 보기에 필요한 개체 형식을 지정합니다. 영화 컨트롤러가 만들어질 때 다음 @model 문이 포함됩니다.

@model MvcMovie.Models.Movie

@model 지시문을 사용하면 컨트롤러가 보기에 전달한 영화에 액세스할 수 있습니다. Model 개체는 강력한 형식입니다. 예를 들어 Details.cshtml 보기에서 코드는 강력한 형식의 Model 개체를 사용하여 DisplayNameForDisplayFor HTML 도우미에 각 영화 필드를 전달합니다. 또한 CreateEdit 메서드와 보기도 Movie 모델 개체를 전달합니다.

Index.cshtml 보기 및 영화 컨트롤러의 Index 메서드를 확인합니다. 코드가 View 메서드를 호출할 때 List 개체를 생성하는 방법에 유의하세요. 이 코드는 Index 작업 메서드에서 보기로 Movies 목록을 전달합니다.

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

영화 컨트롤러를 만들 때 스캐폴딩은 파일 맨 위에 다음 @model 문을 포함했습니다 Index.cshtml .

@model IEnumerable<MvcMovie.Models.Movie>

@model 지시문을 사용하면 강력한 형식인 Model 개체를 사용하여 컨트롤러가 보기에 전달한 영화 목록에 액세스할 수 있습니다. 예를 들어 Index.cshtml 보기에서 코드는 강력한 형식의 Model 개체에 대해 foreach 문을 사용하여 영화를 반복합니다.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

Model 개체가 IEnumerable<Movie> 개체로서 강력한 형식이 지정되었으므로 루프의 각 항목은 Movie 형식입니다. 여러 가지 이점이 있지만 컴파일러는 코드에 사용된 형식이 유효한지도 검사합니다.

추가 리소스

이 자습서에서는 데이터베이스에서 동영상을 관리하기 위한 클래스를 추가합니다. 이러한 클래스는 MVC 앱의 "Model" 부분입니다.

이러한 모델 클래스를 EF Core(Entity Framework Core)(EF Core)와 함께 사용하여 데이터베이스 작업을 수행합니다. EF Core는 작성해야 할 데이터 액세스 코드를 간소화하는 ORM(개체-관계형 매핑) 프레임워크입니다.

만들어진 모델 클래스는 POCO(Plain Old CLR Objects) 클래스라고 합니다. POCO 클래스는 EF Core에 대한 종속성이 없습니다. 이 클래스는 데이터베이스에 저장되는 데이터의 속성만 정의합니다.

이 자습서에서는 먼저 모델 클래스가 작성되며, EF Core가 데이터베이스를 만듭니다.

데이터 모델 클래스 추가

Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가>클래스를 선택합니다. 파일 이름을 Movie.cs로 지정합니다.

Models/Movie.cs 파일을 다음 코드로 업데이트합니다.

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie 클래스에는 데이터베이스에서 기본 키에 필요한 Id 필드가 포함되어 있습니다.

ReleaseDateDataType 특성은 날짜 형식(Date)을 지정합니다. 이 특성을 사용하면:

  • 사용자가 날짜 필드에 시간 정보를 입력할 필요가 없습니다.
  • 시간 정보 없이 날짜만 표시됩니다.

DataAnnotations는 이후 자습서에서 다룹니다.

NuGet 패키지 추가

도구 메뉴에서 NuGet 패키지 관리자> PMC(패키지 관리자 콘솔)를 선택합니다.

PMC 메뉴

PMC에서 다음 명령을 실행합니다.

Install-Package Microsoft.EntityFrameworkCore.Design

앞의 명령은 다음을 추가합니다.

  • EF Core SQL Server 공급자. 이 공급자 패키지는 EF Core 패키지를 종속성으로 설치합니다.
  • 패키지에 사용되는 유틸리티(자습서 뒷부분의 스캐폴딩 단계에서 자동으로 설치됨).

컴파일러 오류에 대한 검사로 프로젝트를 빌드합니다.

영화 페이지 스캐폴드

스캐폴딩 도구를 사용하여 영화 모델에 대한 CRUD(Create, Read, UpdateDelete) 페이지를 생성합니다.

솔루션 탐색기에서 Controllers 폴더를 마우스 오른쪽 단추로 클릭하고 추가> 스캐폴드 항목 새로 만들기를 선택합니다.

위 단계의 보기

스캐폴드 추가 대화 상자에서 Entity Framework를 사용하며 뷰가 포함된 MVC 컨트롤러 > 추가를 선택합니다.

스캐폴드 추가 대화 상자

다음과 같이 Entity Framework를 사용하여 뷰 포함 MVC 컨트롤러 추가 대화 상자의 작업을 완료합니다.

  • 모델 클래스 드롭다운에서 동영상(MvcMovie.Models)을 선택합니다.
  • 데이터 컨텍스트 클래스 행에서 +(더하기) 기호를 선택합니다.
    • 데이터 컨텍스트 추가 대화 상자에서 클래스 이름 MvcMovie.Data.MvcMovieContext가 생성됩니다.
    • 추가를 선택합니다.
  • 컨트롤러 이름: 기본값을 유지합니다.
  • 추가를 선택합니다.

데이터 컨텍스트 추가 기본값 유지

스캐폴딩은 다음과 같이 업데이트를 수행합니다.

  • 프로젝트 파일에 필요한 패키지 참조를 MvcMovie.csproj 삽입합니다.
  • Startup.cs 파일의 Startup.ConfigureServices에 데이터베이스 컨텍스트를 등록합니다.
  • appsettings.json 파일에 데이터베이스 연결 문자열을 추가합니다.

스캐폴딩은 다음을 만듭니다.

  • 영화 컨트롤러: Controllers/MoviesController.cs
  • Create, Delete, Details, EditIndex 페이지에 대한 Razor 뷰 파일: Views/Movies/*.cshtml
  • 데이터베이스 컨텍스트 클래스 추가: Data/MvcMovieContext.cs

이러한 파일 및 파일 업데이트의 자동 생성을 스캐폴딩이라고 합니다.

데이터베이스가 없으므로 아직 스캐폴드된 페이지를 사용할 수 없습니다. 앱을 실행하고 동영상 영화 앱 링크를 선택하면 데이터베이스를 열 수 없음 또는 해당 테이블이 없음: Movie 오류 메시지가 표시됩니다.

초기 마이그레이션

EF Core마이그레이션 기능을 사용하여 데이터베이스를 만듭니다. 마이그레이션은 데이터 모델과 일치하도록 데이터베이스를 만들고 수정할 수 있는 도구 모음입니다.

도구 메뉴에서 NuGet 패키지 관리자>패키지 관리자 콘솔을 선택합니다.

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

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: 마이그레이션 파일을 생성합니다 Migrations/{timestamp}_InitialCreate.cs . InitialCreate 인수는 마이그레이션 이름입니다. 모든 이름을 사용할 수 있지만 규칙에 따라 마이그레이션을 설명하는 이름을 선택합니다. 이는 첫 번째 마이그레이션이므로 생성된 클래스에는 데이터베이스 스키마를 만드는 코드가 포함되어 있습니다. 데이터베이스 스키마는 MvcMovieContext 클래스에 지정된 모델을 기반으로 합니다.

  • Update-Database: 데이터베이스를 이전 명령이 만든 최신 마이그레이션으로 업데이트. 이 명령은 파일에서 Up 메서드를 Migrations/{time-stamp}_InitialCreate.cs 실행하여 데이터베이스를 만듭니다.

Update-Database 명령은 다음과 같은 경고를 생성합니다.

No type was specified for the decimal column ‘Price’ on entity type ‘Movie’. 그러면 값이 기본 전체 자릿수 및 소수 자릿수에 적합하지 않은 경우 자동으로 잘립니다. Explicitly specify the SQL server column type that can accommodate all the values using ‘HasColumnType()’.

위의 경고를 무시하세요. 이 경고는 이후 자습서에서 수정되었습니다.

EF Core의 PMC 도구에 대한 자세한 내용은 EF Core 도구 참조 - Visual Studio의 PMC를 참조하세요.

앱 테스트

앱을 실행하고 영화 앱 링크를 클릭합니다.

다음과 유사한 예외가 발생하면 마이그레이션 단계를 누락했을 수 있습니다.

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

참고 항목

Price 필드에 소수점을 입력하지 못할 수도 있습니다. 소수점으로 쉼표(",")를 사용하는 비 영어 로캘 및 비 US-English 날짜 형식에 대해jQuery 유효성 검사를 지원하려면 앱을 세계화해야 합니다. 세계화 지침은 이 GitHub 문제를 참조하세요.

생성된 데이터베이스 컨텍스트 클래스 및 등록 검사

EF Core에서는 데이터 액세스가 모델을 통해 수행됩니다. 모델은 엔터티 클래스 및 데이터베이스와의 세션을 나타내는 컨텍스트 개체로 구성됩니다. 컨텍스트 개체를 사용하여 데이터를 쿼리하고 저장할 수 있습니다. 데이터베이스 컨텍스트는 Microsoft.EntityFrameworkCore.DbContext에서 파생되며 데이터 모델에 포함할 엔터티를 지정합니다.

스캐폴딩은 데이터베이스 컨텍스트 클래스를 Data/MvcMovieContext.cs 만듭니다.

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<Movie> Movie { get; set; }
    }
}

위의 코드는 데이터베이스의 영화를 나타내는 DbSet<Movie> 속성을 만듭니다.

ASP.NET Core는 DI(종속성 주입)를 사용하여 만들어집니다. 데이터베이스 컨텍스트와 같은 서비스는 Startup에서 DI에 등록해야 합니다. 이러한 서비스가 필요한 구성 요소는 생성자 매개 변수를 통해 제공받습니다.

Controllers/MoviesController.cs 파일에서 생성자는 종속성 주입을 사용하여 MvcMovieContext 컨트롤러에 데이터베이스 컨텍스트를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

스캐폴딩은 Startup.ConfigureServices에서 다음과 같이 강조 표시된 코드를 생성했습니다.

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

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

ASP.NET Core 구성 시스템은 "MvcMovieContext" 데이터베이스 연결 문자열을 읽습니다.

생성된 데이터베이스 연결 문자열 검사

스캐폴딩은 appsettings.json 파일에 연결 문자열을 추가했습니다.

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

로컬 개발의 경우 ASP.NET Core 구성 시스템appsettings.json 파일에서 ConnectionString 키를 읽습니다.

InitialCreate 클래스

마이그레이션 파일을 검사합니다.Migrations/{timestamp}_InitialCreate.cs

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

위의 코드에서

  • InitialCreate.Up은 Movie 테이블을 만들고 Id를 기본 키로 구성합니다.
  • InitialCreate.DownUp 마이그레이션에 의해 변경된 스키마를 되돌립니다.

컨트롤러에서 종속성 주입

Controllers/MoviesController.cs 파일을 열고 생성자를 검사합니다.

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

생성자는 종속성 주입을 사용하여 컨트롤러에 데이터베이스 컨텍스트(MvcMovieContext)를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

Create 페이지를 테스트합니다. 데이터를 입력하고 제출합니다.

Edit, DetailsDelete 링크를 테스트합니다.

강력한 형식의 모델 및 @model 지시문

이 자습서의 앞부분에서는 컨트롤러가 ViewData 사전을 사용하여 데이터 또는 개체를 보기에 전달하는 방법을 살펴봤습니다. ViewData 사전은 정보를 보기에 전달하기 위한 편리한 런타임 바인딩 방법을 제공하는 동적 개체입니다.

MVC는 보기에 강력한 형식의 모델 개체를 전달하는 기능도 제공합니다. 강력한 형식의 방법을 사용하면 컴파일 시에 코드 검사를 수행할 수 있습니다. 스캐폴딩 메커니즘은 MoviesController 클래스 및 뷰에서 강력한 형식의 모델을 전달했습니다.

Controllers/MoviesController.cs 파일에서 생성된 Details 메서드를 검사합니다.

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

일반적으로 id 매개 변수는 경로 데이터 변수로 전달됩니다. 예를 들어 https://localhost:5001/movies/details/1은 다음을 설정합니다.

  • 컨트롤러를 movies 컨트롤러로 설정(첫 번째 URL 세그먼트)
  • 작업을 details로 설정(두 번째 URL 세그먼트)
  • id를 1로 설정(마지막 URL 세그먼트)

id는 다음 예제와 같이 쿼리 문자열을 통해 전달할 수 있습니다.

https://localhost:5001/movies/details?id=1

id 값이 제공되지 않는 경우에 id 매개 변수가 nullable 형식(int?)으로 정의됩니다.

람다 식FirstOrDefaultAsync 메서드에 전달하여 경로 데이터 또는 쿼리 문자열 값과 일치하는 영화 엔터티를 선택합니다.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

영화를 찾으면 Movie 모델의 인스턴스를 Details 보기에 전달합니다.

return View(movie);

Views/Movies/Details.cshtml 파일의 콘텐츠를 검사합니다.

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

보기 파일 맨 위에 있는 @model 문은 보기에 필요한 개체 형식을 지정합니다. 영화 컨트롤러가 만들어질 때 다음 @model 문이 포함됩니다.

@model MvcMovie.Models.Movie

@model 지시문을 사용하면 컨트롤러가 보기에 전달한 영화에 액세스할 수 있습니다. Model 개체는 강력한 형식입니다. 예를 들어 Details.cshtml 보기에서 코드는 강력한 형식의 Model 개체를 사용하여 DisplayNameForDisplayFor HTML 도우미에 각 영화 필드를 전달합니다. 또한 CreateEdit 메서드와 보기도 Movie 모델 개체를 전달합니다.

Index.cshtml 보기 및 영화 컨트롤러의 Index 메서드를 확인합니다. 코드가 View 메서드를 호출할 때 List 개체를 생성하는 방법에 유의하세요. 이 코드는 Index 작업 메서드에서 보기로 Movies 목록을 전달합니다.

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

영화 컨트롤러를 만들 때 스캐폴딩은 파일 맨 위에 다음 @model 문을 포함했습니다 Index.cshtml .

@model IEnumerable<MvcMovie.Models.Movie>

@model 지시문을 사용하면 강력한 형식인 Model 개체를 사용하여 컨트롤러가 보기에 전달한 영화 목록에 액세스할 수 있습니다. 예를 들어 Index.cshtml 보기에서 코드는 강력한 형식의 Model 개체에 대해 foreach 문을 사용하여 영화를 반복합니다.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

Model 개체가 IEnumerable<Movie> 개체로서 강력한 형식이 지정되었으므로 루프의 각 항목은 Movie 형식입니다. 여러 가지 이점이 있지만 컴파일러는 코드에 사용된 형식이 유효한지도 검사합니다.

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 이슈를 참조하세요.

추가 리소스

이 자습서에서는 데이터베이스에서 동영상을 관리하기 위한 클래스를 추가합니다. 이러한 클래스는 MVC 앱의 "Model" 부분입니다.

이러한 모델 클래스를 EF Core(Entity Framework Core)(EF Core)와 함께 사용하여 데이터베이스 작업을 수행합니다. EF Core는 작성해야 할 데이터 액세스 코드를 간소화하는 ORM(개체-관계형 매핑) 프레임워크입니다.

만들어진 모델 클래스는 POCO(Plain Old CLR Objects) 클래스라고 합니다. POCO 클래스는 EF Core에 대한 종속성이 없습니다. 이 클래스는 데이터베이스에 저장되는 데이터의 속성만 정의합니다.

이 자습서에서는 먼저 모델 클래스가 작성되며, EF Core가 데이터베이스를 만듭니다.

데이터 모델 클래스 추가

Models 폴더를 마우스 오른쪽 단추로 클릭하고 추가>클래스를 선택합니다. 파일 이름을 Movie.cs로 지정합니다.

Movie.cs 파일을 다음 코드로 업데이트합니다.

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie 클래스에는 데이터베이스에서 기본 키에 필요한 Id 필드가 포함되어 있습니다.

ReleaseDateDataType 특성은 날짜 형식(Date)을 지정합니다. 이 특성을 사용하면:

  • 사용자가 날짜 필드에 시간 정보를 입력할 필요가 없습니다.
  • 시간 정보 없이 날짜만 표시됩니다.

DataAnnotations는 이후 자습서에서 다룹니다.

NuGet 패키지 추가

도구 메뉴에서 NuGet 패키지 관리자> PMC(패키지 관리자 콘솔)를 선택합니다.

PMC 메뉴

PMC에서 다음 명령을 실행합니다.

Install-Package Microsoft.EntityFrameworkCore.SqlServer

앞의 명령은 EF Core SQL Server 공급자를 추가합니다. 이 공급자 패키지는 EF Core 패키지를 종속성으로 설치합니다. 추가적인 패키지는 자습서 뒷부분의 스캐폴딩 단계에서 자동으로 설치됩니다.

데이터베이스 컨텍스트 클래스 만들기

Movie 모델에 대한 EF Core 기능(생성, 읽기, 수정, 삭제)을 조정하려면 데이터베이스 컨텍스트 클래스가 필요합니다. 데이터베이스 컨텍스트는 데이터 모델에서 Microsoft.EntityFrameworkCore.DbContext 파생되고 데이터 모델에 포함할 엔터티를 지정합니다.

Data 폴더를 만듭니다.

다음 코드를 통해 Data/MvcMovieContext.cs 파일을 추가합니다.

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<Movie> Movie { get; set; }
    }
}

이전 코드에서는 엔터티 집합에 대한 DbSet<Movie> 속성을 만듭니다. Entity Framework 용어에서 엔터티 집합은 일반적으로 데이터베이스 테이블에 해당합니다. 엔터티는 테이블의 행에 해당합니다.

데이터베이스 컨텍스트 등록

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

Startup.cs 맨 위에 다음 using 문을 추가합니다.

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Startup.ConfigureServices에 다음 강조 표시된 코드를 추가합니다.

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

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

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

데이터베이스 연결 문자열 검사

appsettings.json 파일에 연결 문자열을 추가합니다.

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

컴파일러 오류에 대한 검사로 프로젝트를 빌드합니다.

영화 페이지 스캐폴드

스캐폴딩 도구를 사용하여 영화 모델에 대한 CRUD(생성, 읽기, 수정 및 삭제) 페이지를 생성합니다.

솔루션 탐색기에서 Controllers 폴더를 마우스 오른쪽 단추로 클릭하고 추가> 스캐폴드 항목 새로 만들기를 선택합니다.

위 단계의 보기

스캐폴드 추가 대화 상자에서 Entity Framework를 사용하며 뷰가 포함된 MVC 컨트롤러 > 추가를 선택합니다.

스캐폴드 추가 대화 상자

컨트롤러 추가 대화 상자를 입력합니다.

  • 모델 클래스:Movie (MvcMovie.Models)
  • 데이터 컨텍스트 클래스:MvcMovieContext(MvcMovie.Data)

데이터 컨텍스트 추가

  • 보기: 각 옵션의 기본값 선택 유지
  • 컨트롤러 이름: 기본값 MoviesController 유지
  • 추가 선택

Visual Studio가 다음을 만듭니다

  • 영화 컨트롤러(Controllers/MoviesController.cs)
  • 만들기, 삭제, 세부 정보, 편집, Index 페이지에 대한 Razor 뷰 파일(*Views/Movies/`.cshtml`)

이러한 파일의 자동 생성을 스캐폴딩이라고 합니다.

데이터베이스가 없으므로 아직 스캐폴드된 페이지를 사용할 수 없습니다. 앱을 실행하고 동영상 앱 링크를 클릭하면 데이터베이스열 수 없거나 이러한 테이블이 없습니다. 동영상 오류 메시지가 표시됩니다.

초기 마이그레이션

EF Core마이그레이션 기능을 사용하여 데이터베이스를 만듭니다. 마이그레이션은 데이터 모델과 일치하도록 데이터베이스를 만들고 수정할 수 있는 도구 모음입니다.

도구 메뉴에서 NuGet 패키지 관리자> PMC(패키지 관리자 콘솔)를 선택합니다.

PMC에서 다음 명령을 입력합니다.

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate: 마이그레이션 파일을 생성합니다 Migrations/{timestamp}_InitialCreate.cs . InitialCreate 인수는 마이그레이션 이름입니다. 모든 이름을 사용할 수 있지만 규칙에 따라 마이그레이션을 설명하는 이름을 선택합니다. 이는 첫 번째 마이그레이션이므로 생성된 클래스에는 데이터베이스 스키마를 만드는 코드가 포함되어 있습니다. 데이터베이스 스키마는 MvcMovieContext 클래스에 지정된 모델을 기반으로 합니다.

  • Update-Database: 데이터베이스를 이전 명령이 만든 최신 마이그레이션으로 업데이트. 이 명령은 파일에서 Up 메서드를 Migrations/{time-stamp}_InitialCreate.cs 실행하여 데이터베이스를 만듭니다.

    데이터베이스 수정 명령은 다음 경고를 생성합니다.

    No type was specified for the decimal column ‘Price’ on entity type ‘Movie’. 그러면 값이 기본 전체 자릿수 및 소수 자릿수에 적합하지 않은 경우 자동으로 잘립니다. Explicitly specify the SQL server column type that can accommodate all the values using ‘HasColumnType()’.

    이 경고는 무시할 수 있으며 자습서의 뒷부분에서 수정될 예정입니다.

EF Core의 PMC 도구에 대한 자세한 내용은 EF Core 도구 참조 - Visual Studio의 PMC를 참조하세요.

InitialCreate 클래스

마이그레이션 파일을 검사합니다.Migrations/{timestamp}_InitialCreate.cs

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", 
                                 SqlServerValueGenerationStrategy.IdentityColumn),
                Title = table.Column<string>(nullable: true),
                ReleaseDate = table.Column<DateTime>(nullable: false),
                Genre = table.Column<string>(nullable: true),
                Price = table.Column<decimal>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

Up 메서드는 Movie 테이블을 만들고 Id를 기본 키로 구성합니다. Down 메서드는 Up 마이그레이션에 의해 변경된 스키마를 되돌립니다.

앱 테스트

  • 앱을 실행하고 Movie App 링크를 클릭합니다.

    다음 중 하나와 비슷한 예외가 발생할 경우:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

마이그레이션 단계를 누락했을 수 있습니다.

  • Create 페이지를 테스트합니다. 데이터를 입력하고 제출합니다.

    참고 항목

    Price 필드에 소수점을 입력하지 못할 수도 있습니다. 소수점으로 쉼표(",")를 사용하는 비 영어 로캘 및 비 US-English 날짜 형식에 대해jQuery 유효성 검사를 지원하려면 앱을 세계화해야 합니다. 세계화 지침은 이 GitHub 문제를 참조하세요.

  • Edit, DetailsDelete 링크를 테스트합니다.

컨트롤러에서 종속성 주입

Controllers/MoviesController.cs 파일을 열고 생성자를 검사합니다.

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

생성자는 종속성 주입을 사용하여 컨트롤러에 데이터베이스 컨텍스트(MvcMovieContext)를 주입합니다. 컨트롤러의 각 CRUD 메서드에서 해당 데이터베이스 컨텍스트를 사용합니다.

강력한 형식의 모델 및 @model 키워드

이 자습서의 앞부분에서는 컨트롤러가 ViewData 사전을 사용하여 데이터 또는 개체를 보기에 전달하는 방법을 살펴봤습니다. ViewData 사전은 정보를 보기에 전달하기 위한 편리한 런타임 바인딩 방법을 제공하는 동적 개체입니다.

또한 MVC는 확인할 강력한 형식의 모델 개체를 전달하는 기능을 제공합니다. 강력한 형식의 방법을 사용하면 컴파일 시에 코드 검사를 수행할 수 있습니다. 스캐폴딩 메커니즘은 MoviesController 클래스 및 보기에서 이 방법(즉, 강력한 형식의 모델 전달)을 사용합니다.

Controllers/MoviesController.cs 파일에서 생성된 Details 메서드를 검사합니다.

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

일반적으로 id 매개 변수는 경로 데이터 변수로 전달됩니다. 예를 들어 https://localhost:5001/movies/details/1은 다음을 설정합니다.

  • 컨트롤러를 movies 컨트롤러로 설정(첫 번째 URL 세그먼트)
  • 작업을 details로 설정(두 번째 URL 세그먼트)
  • ID를 1로 설정(마지막 URL 세그먼트)

다음과 같이 쿼리 문자열을 사용하여 id에 전달할 수 있습니다.

https://localhost:5001/movies/details?id=1

ID 값이 제공되지 않는 경우에 id 매개 변수가 nullable 형식(int?)으로 정의됩니다.

람다 식FirstOrDefaultAsync에 전달하여 경로 데이터 또는 쿼리 문자열 값과 일치하는 영화 엔터티를 선택합니다.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

영화를 찾으면 Movie 모델의 인스턴스를 Details 보기에 전달합니다.

return View(movie);

Views/Movies/Details.cshtml 파일의 콘텐츠를 검사합니다.

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

보기 파일 맨 위에 있는 @model 문은 보기에 필요한 개체 형식을 지정합니다. 영화 컨트롤러가 만들어질 때 다음 @model 문이 포함됩니다.

@model MvcMovie.Models.Movie

@model 지시문을 사용하면 컨트롤러가 보기에 전달한 영화에 액세스할 수 있습니다. Model 개체는 강력한 형식입니다. 예를 들어 Details.cshtml 보기에서 코드는 강력한 형식의 Model 개체를 사용하여 DisplayNameForDisplayFor HTML 도우미에 각 영화 필드를 전달합니다. 또한 CreateEdit 메서드와 보기도 Movie 모델 개체를 전달합니다.

Index.cshtml 보기 및 영화 컨트롤러의 Index 메서드를 확인합니다. 코드가 View 메서드를 호출할 때 List 개체를 생성하는 방법에 유의하세요. 이 코드는 Index 작업 메서드에서 보기로 Movies 목록을 전달합니다.

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

영화 컨트롤러를 만들 때 스캐폴딩은 파일 맨 위에 다음 @model 문을 포함했습니다 Index.cshtml .

@model IEnumerable<MvcMovie.Models.Movie>

@model 지시문을 사용하면 강력한 형식인 Model 개체를 사용하여 컨트롤러가 보기에 전달한 영화 목록에 액세스할 수 있습니다. 예를 들어 Index.cshtml 보기에서 코드는 강력한 형식의 Model 개체에 대해 foreach 문을 사용하여 영화를 반복합니다.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

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

Model 개체가 강력한 형식이기 때문에(IEnumerable<Movie> 개체처럼) 루프의 각 항목은 Movie 형식입니다. 즉, 여러 가지 이점 중에서도 코드의 컴파일 시 검사를 수행할 수 있다는 뜻입니다.

추가 리소스