다음을 통해 공유


데이터 저장

쿼리를 사용하면 데이터베이스에서 데이터를 읽을 수 있지만, 데이터를 저장하면 데이터베이스에 새 엔터티를 추가하거나, 엔터티를 제거하거나, 어떤 식으로든 기존 엔터티의 속성을 수정할 수 있습니다. EF Core(Entity Framework Core)는 데이터베이스에 데이터를 저장하는 데 두 가지 기본적인 방법을 지원합니다.

방법 1: 변경 내용 추적 및 SaveChanges

대부분의 시나리오에서 프로그램은 데이터베이스에서 일부 데이터를 쿼리하고, 일부 수정을 수행하고, 이러한 수정 내용을 다시 저장해야 합니다. 이를 "작업 단위"라고도 합니다. 예를 들어, 블로그 집합이 있고 그 중 하나의 Url 속성을 변경한다고 가정해 보겠습니다. EF에서는 일반적으로 다음과 같이 수행됩니다.

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Single(b => b.Url == "http://example.com");
    blog.Url = "http://example.com/blog";
    context.SaveChanges();
}

위의 코드는 다음 단계를 수행합니다.

  1. 일반 LINQ 쿼리를 사용하여 데이터베이스에서 엔터티를 로드합니다(쿼리 데이터 참조). EF의 쿼리는 기본적으로 추적되므로 EF는 내부 변경 추적기에 로드된 엔터티를 추적합니다.
  2. 로드된 엔터티 인스턴스는 .NET 속성을 할당하여 평소와 같이 조작됩니다. EF는 이 단계에 관여하지 않습니다.
  3. 마지막으로 DbContext.SaveChanges()가 호출됩니다. 이 시점에서 EF는 엔터티를 로드된 순간의 스냅샷과 비교하여 변경 내용을 자동으로 검색합니다. 검색된 모든 변경 내용은 데이터베이스에 유지됩니다. 관계형 데이터베이스를 사용하는 경우, 일반적으로 관련 행을 업데이트하기 위해 SQL UPDATE를 보내는 작업이 포함됩니다.

위에서는 기존 데이터에 대한 일반적인 업데이트 작업을 설명했지만 엔터티를 추가제거하는 데에도 유사한 원칙이 적용됩니다. DbSet<TEntity>.AddRemove를 호출하여 EF의 변경 추적기와 상호 작용하면 변경 내용이 추적됩니다. 그런 다음 EF는 SaveChanges()가 호출될 때 추적된 모든 변경 내용을 데이터베이스에 적용합니다(예: 관계형 데이터베이스를 사용하는 경우 SQL INSERTDELETE를 통해).

SaveChanges()에는 다음과 같은 이점이 있습니다.

  • 변경된 엔터티 및 속성을 추적하는 코드를 작성할 필요가 없습니다. EF가 자동으로 이 작업을 수행하고 데이터베이스에서 해당 속성만 업데이트하여 성능을 향상합니다. 로드된 엔터티가 UI 구성 요소에 바인딩되어 사용자가 원하는 속성을 변경할 수 있다고 상상해 보세요. EF는 실제로 변경된 엔터티 및 속성을 파악하는 부담을 덜어줍니다.
  • 데이터베이스에 변경 내용을 저장하는 일은 가끔 복잡할 수 있습니다. 예를 들어 해당 블로그에 대한 블로그 및 일부 게시물을 추가하려면 게시물을 삽입하기 전에 삽입된 블로그의 데이터베이스 생성 키를 가져와야 할 수 있습니다(블로그를 참조해야 하므로). EF는 이 모든 작업을 수행하여 복잡성을 없앱니다.
  • EF는 쿼리와 SaveChanges() 간에 다른 사용자가 데이터베이스 행을 수정한 경우와 같은 동시성 문제를 검색할 수 있습니다. 동시성 충돌에서 자세한 내용을 확인할 수 있습니다.
  • 이를 지원하는 데이터베이스에서 SaveChanges()는 트랜잭션의 여러 변경 내용을 자동으로 래핑하여 오류가 발생할 경우 데이터가 일관성을 유지하도록 합니다. 트랜잭션에서 자세한 내용을 확인할 수 있습니다.
  • 또한 SaveChanges()는 여러 변경 내용을 일괄 처리하여 데이터베이스 왕복 횟수를 크게 줄이고 성능을 크게 향상합니다. 효율적인 업데이트에서 자세한 내용을 확인할 수 있습니다.

기본 SaveChanges() 사용에 대한 자세한 내용 및 코드 샘플은 기본 SaveChanges를 참조하세요. EF의 변경 내용 추적에 대한 자세한 내용은 변경 내용 추적 개요를 참조하세요.

방법 2: ExecuteUpdate 및 ExecuteDelete("대량 업데이트")

참고 항목

이 기능은 EF Core 7.0에서 도입되었습니다.

변경 내용 추적 및 SaveChanges()는 변경 내용을 저장하는 강력한 방법이지만 특정한 단점이 있습니다.

먼저, SaveChanges()를 사용하려면 수정하거나 삭제할 모든 엔터티를 쿼리하고 추적해야 합니다. 예를 들어, 특정 임계값 미만의 등급으로 모든 블로그를 삭제해야 하는 경우 잠재적으로 엄청난 수의 행을 쿼리, 구체화 및 추적하고 SaveChanges()가 각 행에 대해 DELETE 문을 생성하도록 해야 합니다. 관계형 데이터베이스는 훨씬 더 효율적인 대안을 제공합니다. 단일 DELETE 명령을 전송하여 WHERE 절을 통해 삭제할 행을 지정할 수 있지만 SaveChanges() 모델에서는 이를 생성할 수 없습니다.

이 "대량 업데이트" 시나리오를 지원하려면 ExecuteDelete를 다음과 같이 사용할 수 있습니다.

context.Blogs.Where(b => b.Rating < 3).ExecuteDelete();

이렇게 하면 일반 LINQ 쿼리와 유사한 일반 LINQ 연산자를 통해 SQL DELETE 문을 표현할 수 있으므로 데이터베이스에 대해 다음 SQL이 실행됩니다.

DELETE FROM [b]
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3

데이터베이스에서 데이터를 로드하거나 EF의 변경 추적기를 포함하지 않고 데이터베이스에서 매우 효율적으로 실행됩니다. 마찬가지로 ExecuteUpdate를 사용하면 SQL UPDATE 문을 표현할 수 있습니다.

엔터티를 대량으로 변경하지 않더라도 변경할 엔터티의 속성을 정확히 알 수 있습니다. 변경 내용 추적 API를 사용하여 변경을 수행하는 것은 지나치게 복잡할 수 있으므로 엔터티 인스턴스를 만들고, Attach를 통해 추적하고, 변경하고, 마지막으로 SaveChanges()를 호출해야 합니다. 이러한 시나리오에서 ExecuteUpdateExecuteDelete는 동일한 작업을 표현하는 훨씬 더 간단한 방법이 될 수 있습니다.

마지막으로 변경 내용 추적과 SaveChanges() 자체에도 특정 런타임 오버헤드가 적용됩니다. 고성능 애플리케이션을 작성하는 경우 ExecuteUpdateExecuteDelete를 사용하면 이러한 구성 요소를 모두 방지하고 원하는 문을 효율적으로 생성할 수 있습니다.

그러나 ExecuteUpdateExecuteDelete에는 특정한 제한 사항도 있습니다.

  • 이러한 메서드는 즉시 실행되며 현재 다른 작업과 함께 일괄 처리할 수 없습니다. 반면, SaveChanges()는 여러 작업을 함께 일괄 처리할 수 있습니다.
  • 변경 내용 추적은 포함되지 않으므로 변경해야 하는 엔터티와 속성을 정확히 알아야 합니다. 즉, 변경할 내용과 변경하지 않을 내용을 더 수동적으로, 하위 수준 코드로 추적해야 할 수 있습니다.
  • 또한 변경 내용 추적은 포함되지 않으므로 이러한 메서드는 변경 내용을 유지할 때 동시성 제어가 자동으로 적용되지 않습니다. 그러나 Where 절을 명시적으로 추가하여 동시성 제어를 직접 구현할 수 있습니다.
  • 현재 업데이트 및 삭제만 지원되며, 삽입은 DbSet<TEntity>.AddSaveChanges()를 통해 수행해야 합니다.

자세한 내용 및 코드 샘플은 ExecuteUpdateExecuteDelete를 참조하세요.

요약

어떤 방법을 언제 사용해야 하는지에 대한 몇 가지 지침은 다음과 같습니다. 절대적인 규칙은 아니지만 유용한 규칙입니다.

  • 어떤 변경이 수행될지 미리 모르는 경우 적용해야 하는 변경 내용을 자동으로 검색하는 SaveChanges를 사용하세요. 예시 시나리오:
    • "데이터베이스에서 블로그를 로드하고 사용자가 변경할 수 있는 양식을 표시하고 싶습니다."
  • 개체 그래프(예: 여러 상호 연결된 개체)를 조작해야 하는 경우 변경 내용의 적절한 순서와 모든 항목을 함께 연결하는 방법을 파악하는 SaveChanges를 사용하세요.
    • "블로그를 업데이트하고, 일부 게시물을 변경하고, 다른 게시물을 삭제하고 싶습니다."
  • 일부 기준에 따라 잠재적으로 많은 수의 엔터티를 변경하려면 ExecuteUpdateExecuteDelete를 사용하세요. 예시 시나리오:
    • "모든 직원의 연봉을 인상하고 싶습니다."
    • "이름이 X로 시작하는 모든 블로그를 삭제하고 싶습니다."
  • 수정하려는 엔터티와 해당 엔터티를 어떻게 변경하고 싶은지 이미 알고 있는 경우 ExecuteUpdateExecuteDelete를 사용하세요. 예시 시나리오:
    • "이름이 'Foo'인 블로그를 삭제하고 싶습니다."
    • "ID가 5인 블로그의 이름을 'Bar'로 변경하고 싶습니다."