다음을 통해 공유


MVC 웹 애플리케이션에 대한 고급 Entity Framework 시나리오(10/10)

작성자: Tom Dykstra

Contoso University 샘플 웹 애플리케이션은 Entity Framework 5 Code First 및 Visual Studio 2012를 사용하여 ASP.NET MVC 4 애플리케이션을 만드는 방법을 보여 줍니다. 자습서 시리즈에 대한 정보는 시리즈의 첫 번째 자습서를 참조하세요.

참고

resolve 수 없는 문제가 발생하면 완료된 장을 다운로드하고 문제를 재현해 보세요. 일반적으로 코드를 완료된 코드와 비교하여 문제에 대한 솔루션을 찾을 수 있습니다. 몇 가지 일반적인 오류 및 해결 방법은 오류 및 해결 방법을 참조하세요.

이전 자습서에서는 리포지토리 및 작업 패턴 단위를 구현했습니다. 이 자습서에서는 다음 항목을 다룹니다.

  • 원시 SQL 쿼리를 수행합니다.
  • 추적 없음 쿼리를 수행합니다.
  • 데이터베이스로 전송된 쿼리를 검사합니다.
  • 프록시 클래스 작업.
  • 변경 내용 자동 검색을 사용하지 않도록 설정합니다.
  • 변경 내용을 저장할 때 유효성 검사를 사용하지 않도록 설정
  • 오류 및 해결 방법

이러한 topics 대부분 이미 만든 페이지로 작업합니다. 원시 SQL을 사용하여 대량 업데이트를 수행하려면 데이터베이스에 있는 모든 과정의 크레딧 수를 업데이트하는 새 페이지를 만듭니다.

과정 크레딧 업데이트 초기 페이지를 보여 주는 스크린샷 숫자 2가 텍스트 필드에 입력됩니다.

추적 없음 쿼리를 사용하려면 부서 편집 페이지에 새 유효성 검사 논리를 추가합니다.

중복된 관리자 오류 메시지가 있는 Contoso University Department Edit 페이지를 보여 주는 스크린샷

원시 SQL 쿼리 수행

Entity Framework Code First API에는 SQL 명령을 데이터베이스에 직접 전달할 수 있는 메서드가 포함되어 있습니다. 다음과 같은 옵션이 있습니다.

  • 엔터티 형식을 반환하는 쿼리에 DbSet.SqlQuery 메서드를 사용합니다. 반환된 개체는 개체에 필요한 DbSet 형식이어야 하며 추적을 해제하지 않는 한 데이터베이스 컨텍스트에서 자동으로 추적됩니다. (메서드에 대한 다음 섹션을 AsNoTracking 참조하세요.)
  • 엔터티가 Database.SqlQuery 아닌 형식을 반환하는 쿼리에 메서드를 사용합니다. 이 메서드를 사용하여 엔터티 형식을 검색하더라도 반환된 데이터는 데이터베이스 컨텍스트에 의해 추적되지 않습니다.
  • 쿼리가 아닌 명령에 는 Database.ExecuteSqlCommand 를 사용합니다.

Entity Framework를 사용할 때 장점 중 하나는 코드가 데이터를 저장하는 특정 메서드에 너무 얽매이지 않아도 된다는 점입니다. 이것은 사용자를 위한 SQL 쿼리와 명령이 생성되므로 가능하며 사용자는 코드를 직접 작성할 필요가 없습니다. 그러나 수동으로 만든 특정 SQL 쿼리를 실행해야 하는 경우 예외적인 시나리오가 있으며 이러한 메서드를 사용하면 이러한 예외를 처리할 수 있습니다.

웹 애플리케이션에서 SQL 명령을 실행할 때 항상 그렇듯이 SQL 삽입 공격으로부터 사이트를 보호하기 위한 예방 조치를 취해야 합니다. 이를 수행하는 한 가지 방법은 매개 변수가 있는 쿼리를 사용하여 웹 페이지에서 제출한 문자열을 SQL 명령으로 해석할 수 없도록 하는 것입니다. 이 자습서에서는 사용자 입력을 쿼리에 통합할 때 매개 변수가 있는 쿼리를 사용합니다.

엔터티를 반환하는 쿼리 호출

추가 메서드를 GenericRepository 사용하여 파생 클래스를 만들 필요 없이 클래스가 추가 필터링 및 정렬 유연성을 제공하도록 하려는 경우를 가정해 보겠습니다. 이를 달성하는 한 가지 방법은 SQL 쿼리를 허용하는 메서드를 추가하는 것입니다. 그런 다음 조인 또는 하위 쿼리에 종속된 절과 같이 Where 컨트롤러에서 원하는 모든 종류의 필터링 또는 정렬을 지정할 수 있습니다. 이 섹션에서는 이러한 메서드를 구현하는 방법을 알아보세요.

GetWithRawSqlGenericRepository.cs에 다음 코드를 추가하여 메서드를 만듭니다.

public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
    return dbSet.SqlQuery(query, parameters).ToList();
}

다음 예제와 같이 CourseController.cs에서 메서드에서 Details 새 메서드를 호출합니다.

public ActionResult Details(int id)
{
    var query = "SELECT * FROM Course WHERE CourseID = @p0";
    return View(unitOfWork.CourseRepository.GetWithRawSql(query, id).Single());
}

이 경우 메서드를 GetByID 사용할 수 있지만 메서드를 사용하여 메서드가 GetWithRawSql 작동하는지 확인 GetWithRawSQL 합니다.

세부 정보 페이지를 실행하여 선택 쿼리가 작동하는지 확인합니다( 과정 탭을 선택한 다음, 한 과정에 대한 세부 정보 선택).

Contoso University 세부 정보 페이지를 보여 주는 스크린샷

다른 형식의 개체를 반환하는 쿼리 호출

이전에 등록 날짜별로 학생 수를 보여주는 [정보] 페이지의 학생 통계 표를 만들었습니다. HomeController.cs에서 이 작업을 수행하는 코드는 LINQ를 사용합니다.

var data = from student in db.Students
           group student by student.EnrollmentDate into dateGroup
           select new EnrollmentDateGroup()
           {
               EnrollmentDate = dateGroup.Key,
               StudentCount = dateGroup.Count()
           };

LINQ를 사용하는 대신 SQL에서 직접 이 데이터를 검색하는 코드를 작성한다고 가정합니다. 이렇게 하려면 엔터티 개체 이외의 항목을 반환하는 쿼리를 실행해야 합니다. 즉, 메서드를 Database.SqlQuery 사용해야 합니다.

HomeController.cs에서 메서드의 LINQ 문을 About 다음 코드로 바꿉니다.

var query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
    + "FROM Person "
    + "WHERE EnrollmentDate IS NOT NULL "
    + "GROUP BY EnrollmentDate";
var data = db.Database.SqlQuery<EnrollmentDateGroup>(query);

정보 페이지를 실행합니다. 이전과 동일한 데이터가 표시됩니다.

Contoso University 정보 페이지를 보여 주는 스크린샷

업데이트 쿼리 호출

Contoso University 관리자가 모든 과정의 학점 수를 변경하는 등 데이터베이스에서 대량 변경을 수행할 수 있기를 원한다고 가정합니다. 대학에 과목이 많은 경우 엔터티로 모두 검색하여 개별적으로 변경하는 것은 비효율적입니다. 이 섹션에서는 사용자가 모든 과정의 크레딧 수를 변경할 요소를 지정할 수 있는 웹 페이지를 구현하고 SQL UPDATE 문을 실행하여 변경합니다. 그러면 웹 페이지가 다음 그림과 같이 표시됩니다.

과정 크레딧 업데이트 초기 페이지를 보여 주는 스크린샷 숫자 2가 텍스트 필드에 입력됩니다.

이전 자습서에서는 제네릭 리포지토리를 사용하여 컨트롤러의 엔터티를 읽고 업데이트 Course 했습니다 Course . 이 대량 업데이트 작업의 경우 제네릭 리포지토리에 없는 새 리포지토리 메서드를 만들어야 합니다. 이렇게 하려면 클래스에서 파생되는 전용 CourseRepository 클래스를 GenericRepository 만듭니다.

DAL 폴더에서 CourseRepository.cs를 만들고 기존 코드를 다음 코드로 바꿉니다.

using System;
using ContosoUniversity.Models;

namespace ContosoUniversity.DAL
{
    public class CourseRepository : GenericRepository<Course>
    {
        public CourseRepository(SchoolContext context)
            : base(context)
        {
        }

        public int UpdateCourseCredits(int multiplier)
        {
            return context.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier);
        }

    }
}

UnitOfWork.cs에서 리포지토리 형식을 Course 에서 로 GenericRepository<Course> 변경합니다.CourseRepository:

private CourseRepository courseRepository;
public CourseRepository CourseRepository
{
    get
    {

        if (this.courseRepository == null)
        {
            this.courseRepository = new CourseRepository(context);
        }
        return courseRepository;
    }
}

CourseController.cs에서 메서드를 UpdateCourseCredits 추가합니다.

public ActionResult UpdateCourseCredits(int? multiplier)
{
    if (multiplier != null)
    {
        ViewBag.RowsAffected = unitOfWork.CourseRepository.UpdateCourseCredits(multiplier.Value);
    }
    return View();
}

이 메서드는 및 HttpPost모두 HttpGet 에 사용됩니다. 메서드가 HttpGetUpdateCourseCredits 실행되면 변수는 multiplier null이 되고 보기에는 앞의 그림과 같이 빈 텍스트 상자와 제출 단추가 표시됩니다.

업데이트 단추를 클릭하고 메서드가 HttpPost 실행 multiplier 되면 텍스트 상자에 값이 입력됩니다. 그런 다음 코드는 영향을 받는 행 수를 반환하는 리포지 UpdateCourseCredits 토리 메서드를 호출하고 해당 값은 개체에 ViewBag 저장됩니다. 보기가 개체의 영향을 받는 행 ViewBag 수를 받으면 다음 그림과 같이 텍스트 상자와 제출 단추 대신 해당 번호가 표시됩니다.

영향을 받는 Contoso University 업데이트 과정 크레딧 행 페이지를 보여 주는 스크린샷

과정 크레딧 업데이트 페이지의 Views\Course 폴더에 보기를 만듭니다.

보기 추가 대화 상자를 보여 주는 스크린샷 업데이트 과정 크레딧은 보기 이름 텍스트 필드에 입력됩니다.

Views\Course\UpdateCourseCredits.cshtml에서 템플릿 코드를 다음 코드로 바꿉니다.

@model ContosoUniversity.Models.Course

@{
    ViewBag.Title = "UpdateCourseCredits";
}

<h2>Update Course Credits</h2>

@if (ViewBag.RowsAffected == null)
{
    using (Html.BeginForm())
    {
        <p>
            Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
        </p>
        <p>
            <input type="submit" value="Update" />
        </p>
    }
}
@if (ViewBag.RowsAffected != null)
{
    <p>
        Number of rows updated: @ViewBag.RowsAffected
    </p>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Courses(과정) 탭을 선택하여 UpdateCourseCredits 메서드를 실행한 후 브라우저의 주소 표시줄에서 URL 끝에 "/UpdateCourseCredits"를 추가합니다(예: http://localhost:50205/Course/UpdateCourseCredits). 텍스트 상자에 숫자를 입력합니다.

텍스트 필드에 숫자 2가 입력된 과정 크레딧 업데이트 초기 페이지를 보여 주는 스크린샷

업데이트를 클릭합니다. 영향을 받은 행 수가 표시됩니다.

업데이트된 행 수가 있는 과정 크레딧 업데이트 페이지를 보여 주는 스크린샷

학점 수가 수정된 과정 목록을 보려면 목록으로 돌아가기를 클릭합니다.

과정 인덱스 페이지를 보여 주는 스크린샷 수정된 크레딧 수와 함께 과정 목록이 표시됩니다.

원시 SQL 쿼리에 대한 자세한 내용은 Entity Framework 팀 블로그의 원시 SQL 쿼리 를 참조하세요.

비 추적 쿼리

데이터베이스 컨텍스트는 데이터베이스 행을 검색하고 이를 나타내는 엔터티 개체를 만들 때 기본적으로 메모리의 엔터티가 데이터베이스의 엔터티와 동기화되어 있는지 여부를 추적합니다. 메모리의 데이터는 캐시의 역할을 하고 엔터티를 업데이트할 때 사용됩니다. 컨텍스트 인스턴스는 일반적으로 수명이 짧으며(각 요청에 대해 새 것이 만들어지고 삭제됨) 엔터티를 읽는 컨텍스트는 일반적으로 해당 엔터티가 다시 사용되기 전에 삭제되므로 이 캐싱은 웹 애플리케이션에 종종 필요하지 않습니다.

컨텍스트가 메서드를 사용하여 AsNoTracking 쿼리에 대한 엔터티 개체를 추적하는지 여부를 지정할 수 있습니다. 이러한 작업을 수행할 수 있는 일반적인 시나리오는 다음을 포함합니다.

  • 쿼리는 추적을 해제하면 성능이 크게 향상될 수 있는 대량의 데이터를 검색합니다.
  • 엔터티를 업데이트하기 위해 연결하려고 하지만 이전에 다른 용도로 동일한 엔터티를 검색했습니다. 엔터티는 데이터베이스 컨텍스트에서 이미 추적 중이므로 변경하려는 엔터티를 연결할 수 없습니다. 이 문제를 방지하는 한 가지 방법은 이전 쿼리와 AsNoTracking 함께 옵션을 사용하는 것입니다.

이 섹션에서는 두 번째 시나리오를 보여 주는 비즈니스 논리를 구현합니다. 특히 강사가 둘 이상의 부서의 관리자가 될 수 없다는 비즈니스 규칙을 적용합니다.

DepartmentController.cs에서 및 Create 메서드에서 호출할 수 있는 새 메서드를 Edit 추가하여 두 부서에 동일한 관리자가 없는지 확인합니다.

private void ValidateOneAdministratorAssignmentPerInstructor(Department department)
{
    if (department.PersonID != null)
    {
        var duplicateDepartment = db.Departments
            .Include("Administrator")
            .Where(d => d.PersonID == department.PersonID)
            .FirstOrDefault();
        if (duplicateDepartment != null && duplicateDepartment.DepartmentID != department.DepartmentID)
        {
            var errorMessage = String.Format(
                "Instructor {0} {1} is already administrator of the {2} department.",
                duplicateDepartment.Administrator.FirstMidName,
                duplicateDepartment.Administrator.LastName,
                duplicateDepartment.Name);
            ModelState.AddModelError(string.Empty, errorMessage);
        }
    }
}

유효성 검사 오류가 없는 경우 메서드 블록에 tryHttpPostEdit 코드를 추가하여 이 새 메서드를 호출합니다. 이제 블록은 try 다음 예제와 같습니다.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
   [Bind(Include = "DepartmentID, Name, Budget, StartDate, RowVersion, PersonID")]
    Department department)
{
   try
   {
      if (ModelState.IsValid)
      {
         ValidateOneAdministratorAssignmentPerInstructor(department);
      }

      if (ModelState.IsValid)
      {
         db.Entry(department).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DbUpdateConcurrencyException ex)
   {
      var entry = ex.Entries.Single();
      var clientValues = (Department)entry.Entity;

부서 편집 페이지를 실행하고 부서의 관리자를 이미 다른 부서의 관리자인 강사로 변경합니다. 예상되는 오류 메시지가 표시됩니다.

중복된 관리자 오류 메시지가 있는 부서 편집 페이지를 보여 주는 스크린샷

이제 부서 편집 페이지를 다시 실행하고 이번에는 예산 금액을 변경합니다. 저장을 클릭하면 오류 페이지가 표시됩니다.

개체 상태 관리자 오류 메시지가 있는 부서 편집 페이지를 보여 주는 스크린샷

예외 오류 메시지는 다음과 같은 이벤트 시퀀스로 인해 발생한 "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."입니다.

  • 메서드는 Edit 메서드를 ValidateOneAdministratorAssignmentPerInstructor 호출하여 Kim Abercrombie가 관리자로 있는 모든 부서를 검색합니다. 따라서 영어 부서를 읽게 됩니다. 편집 중인 부서이므로 오류가 보고되지 않습니다. 그러나 이 읽기 작업의 결과로 데이터베이스에서 읽은 영어 부서 엔터티는 이제 데이터베이스 컨텍스트에서 추적되고 있습니다.
  • 메서드는 Edit MVC 모델 바인더에서 만든 영어 부서 엔터티에 플래그를 설정 Modified 하려고 하지만 컨텍스트가 이미 영어 부서의 엔터티를 추적하고 있기 때문에 실패합니다.

이 문제에 대한 한 가지 해결 방법은 컨텍스트가 유효성 검사 쿼리에서 검색된 메모리 내 부서 엔터티를 추적하지 못하도록 하는 것입니다. 이 엔터티를 업데이트하거나 메모리에 캐시되는 것이 도움이 되는 방식으로 다시 읽지 않기 때문에 이 작업을 수행하는 데는 단점이 없습니다.

DepartmentController.csValidateOneAdministratorAssignmentPerInstructor 메서드에서 다음과 같이 추적을 지정하지 않습니다.

var duplicateDepartment = db.Departments
   .Include("Administrator")
   .Where(d => d.PersonID == department.PersonID)
   .AsNoTracking()
   .FirstOrDefault();

부서의 예산 금액을 편집하려는 시도를 반복합니다. 이번에는 작업이 성공하고 사이트는 수정된 예산 값을 보여 주는 부서 인덱스 페이지로 예상대로 반환됩니다.

데이터베이스로 전송된 쿼리 검사

때로는 데이터베이스로 전송된 실제 SQL 쿼리를 볼 수 있는 것이 도움이 됩니다. 이렇게 하려면 디버거에서 쿼리 변수를 검사하거나 쿼리의 ToString 메서드를 호출할 수 있습니다. 이를 시도하려면 간단한 쿼리를 살펴본 다음 즉시 로드, 필터링 및 정렬과 같은 옵션을 추가할 때 어떤 일이 발생하는지 살펴보겠습니다.

Controllers/CourseController에서 메서드를 Index 다음 코드로 바꿉니다.

public ViewResult Index()
{
    var courses = unitOfWork.CourseRepository.Get();
    return View(courses.ToList());
}

이제 메서드의 및 return orderBy(query).ToList(); 문에 있는 GenericRepository.csGet 에서 return query.ToList(); 중단점을 설정합니다. 디버그 모드에서 프로젝트를 실행하고 과정 인덱스 페이지를 선택합니다. 코드가 중단점에 도달하면 변수를 검사합니다 query . SQL Server 전송된 쿼리가 표시됩니다. 간단한 Select 문입니다.

{SELECT 
[Extent1].[CourseID] AS [CourseID], 
[Extent1].[Title] AS [Title], 
[Extent1].[Credits] AS [Credits], 
[Extent1].[DepartmentID] AS [DepartmentID]
FROM [Course] AS [Extent1]}

샘플 웹 애플리케이션 일반 리포지토리 탭을 보여 주는 스크린샷 쿼리 변수가 선택됩니다.

쿼리가 너무 길어 Visual Studio의 디버깅 창에 표시할 수 없습니다. 전체 쿼리를 보려면 변수 값을 복사하여 텍스트 편집기에 붙여넣을 수 있습니다.

선택할 때 드롭다운 메뉴가 표시된 변수 값을 보여 주는 스크린샷 값 복사 옵션이 강조 표시됩니다.

이제 사용자가 특정 부서를 필터링할 수 있도록 과정 인덱스 페이지에 드롭다운 목록을 추가합니다. 강좌를 제목별로 정렬하고 탐색 속성에 대한 즉시 로드를 Department 지정합니다. CourseController.cs에서 메서드를 Index 다음 코드로 바꿉니다.

public ActionResult Index(int? SelectedDepartment)
{
    var departments = unitOfWork.DepartmentRepository.Get(
        orderBy: q => q.OrderBy(d => d.Name));
    ViewBag.SelectedDepartment = new SelectList(departments, "DepartmentID", "Name", SelectedDepartment);

    int departmentID = SelectedDepartment.GetValueOrDefault(); 
    return View(unitOfWork.CourseRepository.Get(
        filter: d => !SelectedDepartment.HasValue || d.DepartmentID == departmentID,
        orderBy: q => q.OrderBy(d => d.CourseID),
        includeProperties: "Department"));
}

메서드는 매개 변수에서 드롭다운 목록의 선택한 값을 받습니다 SelectedDepartment . 아무것도 선택하지 않으면 이 매개 변수는 null이 됩니다.

SelectList 모든 부서를 포함하는 컬렉션이 드롭다운 목록의 보기로 전달됩니다. 생성자에 전달된 SelectList 매개 변수는 값 필드 이름, 텍스트 필드 이름 및 선택한 항목을 지정합니다.

Get 리포지토리의 메서드에 Course 대해 코드는 탐색 속성에 대한 Department 필터 식, 정렬 순서 및 즉시 로드를 지정합니다. 드롭다운 목록에서 아무 것도 선택하지 않으면 필터 식이 항상 반환 true 됩니다(즉, SelectedDepartment null임).

Views\Course\Index.cshtml에서 여 table 는 태그 바로 앞에 다음 코드를 추가하여 드롭다운 목록과 제출 단추를 만듭니다.

@using (Html.BeginForm())
{
    <p>Select Department: @Html.DropDownList("SelectedDepartment","All")   
    <input type="submit" value="Filter" /></p>
}

중단점이 클래스에 설정된 상태에서 GenericRepository 과정 인덱스 페이지를 실행합니다. 코드가 중단점에 도달하면 페이지가 브라우저에 표시되도록 처음 두 번 계속 진행합니다. 드롭다운 목록에서 부서를 선택하고 필터를 클릭합니다.

경제부가 선택된 과정 인덱스 페이지를 보여 주는 스크린샷

이번에는 드롭다운 목록에 대한 부서 쿼리에 대한 첫 번째 중단점이 됩니다. 이를 건너뛰고 다음에 코드가 query 중단점에 도달할 때 변수를 확인하여 쿼리의 Course 모양을 확인합니다. 다음과 같은 내용이 표시됩니다.

{SELECT 
[Extent1].[CourseID] AS [CourseID], 
[Extent1].[Title] AS [Title], 
[Extent1].[Credits] AS [Credits], 
[Extent1].[DepartmentID] AS [DepartmentID], 
[Extent2].[DepartmentID] AS [DepartmentID1], 
[Extent2].[Name] AS [Name], 
[Extent2].[Budget] AS [Budget], 
[Extent2].[StartDate] AS [StartDate], 
[Extent2].[PersonID] AS [PersonID], 
[Extent2].[Timestamp] AS [Timestamp]
FROM  [Course] AS [Extent1]
INNER JOIN [Department] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]
WHERE (@p__linq__0 IS NULL) OR ([Extent1].[DepartmentID] = @p__linq__1)}

이제 JOIN 쿼리가 데이터와 Course 함께 데이터를 로드 Department 하고 절을 포함하는 쿼리임을 WHERE 알 수 있습니다.

프록시 클래스 작업

Entity Framework에서 엔터티 인스턴스를 만들 때(예: 쿼리를 실행할 때) 엔터티에 대한 프록시 역할을 하는 동적으로 생성된 파생 형식의 인스턴스로 만드는 경우가 많습니다. 이 프록시는 속성에 액세스할 때 자동으로 작업을 수행하기 위한 후크를 삽입하도록 엔터티의 일부 가상 속성을 재정의합니다. 예를 들어 이 메커니즘은 관계의 지연 로드를 지원하는 데 사용됩니다.

대부분의 경우 이러한 프록시 사용을 인식할 필요가 없지만 예외가 있습니다.

  • 일부 시나리오에서는 Entity Framework가 프록시 인스턴스를 만들지 못하도록 방지할 수 있습니다. 예를 들어 프록시가 아닌 인스턴스를 직렬화하는 것이 프록시 인스턴스를 직렬화하는 것보다 더 효율적일 수 있습니다.
  • 연산자를 사용하여 엔터티 클래스를 new 인스턴스화할 때 프록시 instance 가져옵니다. 즉, 지연 로드 및 자동 변경 내용 추적과 같은 기능을 얻을 수 없습니다. 일반적으로 괜찮습니다. 일반적으로 데이터베이스에 없는 새 엔터티를 만들고 엔터티를 로 명시적으로 표시하는 경우 변경 내용 추적이 필요하지 않으므로 지연 로드가 Added필요하지 않습니다. 그러나 지연 로드가 필요하고 변경 내용 추적이 필요한 경우 클래스의 메서드를 사용하여 프록시를 사용하여 새 엔터티 인스턴스를 CreateDbSet 만들 수 있습니다.
  • 프록시 형식에서 실제 엔터티 형식을 가져올 수 있습니다. 클래스의 메서드를 GetObjectTypeObjectContext 사용하여 instance 프록시 형식의 실제 엔터티 형식을 가져올 수 있습니다.

자세한 내용은 Entity Framework 팀 블로그에서 프록시 작업을 참조하세요.

변경 내용 자동 검색을 사용하지 않도록 설정

Entity Framework는 엔터티의 현재 값을 원래 값과 비교하여 엔터티가 변경된 방법(즉, 데이터베이스에 전송해야 하는 업데이트)을 결정합니다. 원래 값은 엔터티가 쿼리되거나 연결되었을 때 저장됩니다. 자동 변경 내용 검색을 발생시키는 일부 메서드는 다음과 같습니다.

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

많은 수의 엔터티를 추적하고 루프에서 이러한 메서드 중 하나를 여러 번 호출하는 경우 AutoDetectChangesEnabled 속성을 사용하여 자동 변경 검색을 일시적으로 해제하여 성능이 크게 향상될 수 있습니다. 자세한 내용은 변경 내용 자동 검색을 참조하세요.

변경 내용을 저장할 때 유효성 검사 사용 안 함

메서드를 SaveChanges 호출할 때 기본적으로 Entity Framework는 데이터베이스를 업데이트하기 전에 변경된 모든 엔터티의 모든 속성에서 데이터의 유효성을 검사합니다. 많은 수의 엔터티를 업데이트했고 이미 데이터의 유효성을 검사한 경우 이 작업은 필요하지 않으며, 일시적으로 유효성 검사를 해제하여 변경 내용을 저장하는 프로세스에 시간이 덜 걸릴 수 있습니다. ValidateOnSaveEnabled 속성을 사용하여 이 작업을 수행할 수 있습니다. 자세한 내용은 유효성 검사를 참조하세요.

요약

이렇게 하면 ASP.NET MVC 애플리케이션에서 Entity Framework를 사용하는 방법에 대한 이 자습서 시리즈가 완료됩니다. 다른 Entity Framework 리소스에 대한 링크는 ASP.NET 데이터 액세스 콘텐츠 맵에서 찾을 수 있습니다.

웹 애플리케이션을 빌드한 후 배포하는 방법에 대한 자세한 내용은 MSDN 라이브러리의 배포 콘텐츠 맵 ASP.NET 참조하세요.

인증 및 권한 부여와 같은 MVC와 관련된 다른 topics 대한 자세한 내용은 MVC 권장 리소스를 참조하세요.

감사의 글

  • Tom Dykstra는 이 자습서의 원래 버전을 작성했으며 Microsoft 웹 플랫폼 및 도구 콘텐츠 팀의 선임 프로그래밍 작성자입니다.
  • Rick Anderson (twitter @RickAndMSFT)은 이 자습서를 공동 저술했으며 대부분의 작업을 EF 5 및 MVC 4용으로 업데이트했습니다. Rick은 Azure 및 MVC에 중점을 둔 Microsoft의 선임 프로그래밍 작성자입니다.
  • Rowan Miller 와 Entity Framework 팀의 다른 구성원은 EF 5에 대한 자습서를 업데이트하는 동안 발생한 마이그레이션과 관련된 많은 문제를 코드 검토를 지원하고 디버그하는 데 도움을 주었습니다.

VB

자습서가 원래 생성되었을 때 완료된 다운로드 프로젝트의 C# 및 VB 버전을 모두 제공했습니다. 이 업데이트를 통해 각 챕터에 대해 C# 다운로드 가능한 프로젝트를 제공하여 시리즈의 어느 곳에서나 쉽게 시작할 수 있지만 시간 제한 및 기타 우선 순위로 인해 VB에 대해 이를 수행하지 않았습니다. 이러한 자습서를 사용하여 VB 프로젝트를 빌드하고 다른 사용자와 공유하려는 경우 알려주세요.

오류 및 해결 방법

만들 수 없음/섀도 복사본

오류 메시지:

해당 파일이 이미 있는 경우 'DotNetOpenAuth.OpenId'를 만들거나 섀도 복사본을 만들 수 없습니다.

해결책:

몇 초 정도 기다렸다가 페이지를 새로 고칩니다.

Update-Database 인식되지 않음

오류 메시지:

'Update-Database'라는 용어는 cmdlet, 함수, 스크립트 파일 또는 작동 가능한 프로그램의 이름으로 인식되지 않습니다. 이름의 철자를 확인하거나 경로가 포함되어 있으면 경로가 올바른지 확인하고 다시 시도합니다.(PMC의 Update-Database 명령에서)

해결책:

Visual Studio를 끝냅니다. 프로젝트를 다시 열고 다시 시도합니다.

유효성 검사 실패

오류 메시지:

하나 이상의 엔터티에 대한 유효성 검사에 실패했습니다. 자세한 내용은 'EntityValidationErrors' 속성을 참조하세요. (PMC의 Update-Database 명령에서)

해결책:

이 문제의 한 가지 원인은 메서드가 실행될 때 유효성 검사 오류입니다 Seed . 메서드 디버깅에 대한 팁은 EF(Entity Framework) DB 시드 및 디버깅을 Seed 참조하세요.

HTTP 500.19 오류

오류 메시지:

HTTP 오류 500.19 - 내부 서버 오류
페이지의 관련 구성 데이터가 잘못되어 요청된 페이지에 액세스할 수 없습니다.

해결책:

이 오류를 가져올 수 있는 한 가지 방법은 솔루션의 복사본을 여러 개 갖는 것이며, 각 복사본은 동일한 포트 번호를 사용하는 것입니다. 일반적으로 Visual Studio의 모든 인스턴스를 종료한 다음 작업 중인 프로젝트를 다시 시작하여 이 문제를 해결할 수 있습니다. 작동하지 않으면 포트 번호를 변경해 보세요. 프로젝트 파일을 마우스 오른쪽 단추로 클릭한 다음 속성을 클릭합니다. 탭을 선택한 다음 프로젝트 URL 텍스트 상자에서 포트 번호를 변경합니다.

SQL Server 인스턴스 찾기 오류

오류 메시지:

SQL Server에 연결하는 중에 네트워크 관련 오류 또는 인스턴스별 오류가 발생했습니다. 서버를 찾을 수 없거나 액세스할 수 없습니다. 인스턴스 이름이 올바르고 SQL Server가 원격 연결을 허용하도록 구성되어 있는지 확인합니다. (공급자: SQL 네트워크 인터페이스, 오류: 26 - 지정된 서버/인스턴스 찾기 오류)

해결책:

연결 문자열을 확인합니다. 데이터베이스를 수동으로 삭제한 경우 생성 문자열에서 데이터베이스 이름을 변경합니다.