변경 내용 저장 및 동시성 관리(Entity Framework)

기본적으로 Entity Framework 에서는 낙관적 동시성 모델을 구현합니다. 다시 말해서, 데이터가 쿼리된 후 업데이트될 때까지 데이터 소스에서 데이터에 대한 잠금이 유지되지 않습니다. Entity Framework 는 동시성 확인 없이 개체 변경 내용을 데이터베이스에 저장합니다. 동시성이 높을 가능성이 있는 엔터티의 경우, 아래의 예제처럼 개념적 계층에서 ConcurrencyMode="fixed" 특성과 함께 엔터티 속성을 정의하는 것이 좋습니다.

<Property Name="Status" Type="Byte" Nullable="false" ConcurrencyMode="Fixed" />

이 특성이 사용되면 Entity Framework 는 변경 내용을 데이터베이스에 저장하기 전에 데이터베이스에서 변경 내용이 있는지 확인합니다. 충돌하는 변경이 있으면 OptimisticConcurrencyException이 발생합니다. 자세한 내용은 방법: 개체 컨텍스트의 데이터 동시성 관리(Entity Framework)를 참조하십시오. 저장 프로시저를 사용하여 데이터 원본에 업데이트하는 엔터티 데이터 모델 을 정의하면 OptimisticConcurrencyException이 발생할 수도 있습니다. 이런 경우, 업데이트를 수행하는 데 사용되는 저장 프로시저에서 업데이트된 행이 없다고 보고하면 예외가 발생합니다.

이렇게 동시성이 높은 시나리오에서 업데이트할 경우 Refresh를 자주 호출하는 것이 좋습니다. Refresh를 호출할 때 RefreshMode는 변경 전파 방식을 제어합니다. StoreWins 옵션을 사용하면 Entity Framework 는 개체 캐시의 모든 데이터를 데이터베이스의 해당 값으로 덮어씁니다. 그와 반대로, ClientWins 옵션은 캐시의 원래 값을 데이터 원본의 최신 값으로 바꿉니다. 그러면 캐시의 데이터에 대한 변경 내용과 데이터 원본의 동일한 데이터에 대해 수행한 변경 내용 사이의 충돌을 제거함으로써, 개체 캐시의 모든 변경된 데이터가 다시 데이터 원본에 제대로 저장될 수 있습니다.

데이터 원본 업데이트로 인해 개체 컨텍스트 내의 다른 개체에 속한 데이터가 수정될 수 있는 경우 SaveChanges 메서드 호출 후 Refresh 메서드를 호출합니다. 예를 들어, AdventureWorks Sales 모델에서 새 SalesOrderDetail이 추가되면 트리거가 발생하여 새 항목이 포함된 부분합이 반영되도록 SubTotal 열이 업데이트됩니다. 이 경우 Refresh 메서드가 호출되고 해당 주문에 대해 SalesOrderHeader 개체가 전달됩니다. 그러면 트리거에 의해 생성된 값이 개체 컨텍스트의 SalesOrderHeader 개체로 다시 전송됩니다.

Entity Framework 는 캐시의 개체에 적용된 변경 내용을 추적합니다. SaveChanges 메서드가 호출되면 Entity Framework 는 변경 내용을 다시 데이터 소스에 병합하려고 시도합니다. 개체 캐시의 데이터 변경이 캐시에서 개체가 추가되거나 새로 고쳐진 후 데이터 소스에 적용된 변경과 충돌할 경우, SaveChangesOptimisticConcurrencyException이 발생하면서 실패할 수 있습니다. 그러면 전체 트랜잭션이 롤백됩니다. OptimisticConcurrencyException이 발생하는 경우, 다음 예제와 같이 Refresh를 호출하고 개체 데이터의 데이터를 유지하는 방법(ClientWins)으로 충돌을 해결할지 아니면 개체 캐시를 데이터 원본의 데이터로 업데이트하는 방법(StoreWins)으로 해결할 것인지 지정합니다.

Try
    ' Try to save changes, which may cause a conflict. 
    Dim num As Integer = context.SaveChanges()
    Console.WriteLine("No conflicts. " & num.ToString() & " updates saved.")
Catch generatedExceptionName As OptimisticConcurrencyException
    ' Resolve the concurrency conflict by refreshing the 
    ' object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders)

    ' Save changes. 
    context.SaveChanges()
    Console.WriteLine("OptimisticConcurrencyException handled and changes saved")
End Try
try
{
    // Try to save changes, which may cause a conflict.
    int num = context.SaveChanges();
    Console.WriteLine("No conflicts. " +
        num.ToString() + " updates saved.");
}
catch (OptimisticConcurrencyException)
{
    // Resolve the concurrency conflict by refreshing the 
    // object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders);

    // Save changes.
    context.SaveChanges();
    Console.WriteLine("OptimisticConcurrencyException "
    + "handled and changes saved");
}

ObjectContext에 추가된 개체가 데이터 원본에서 성공적으로 생성되지 않으면 SaveChangesUpdateException을 생성할 수 있습니다. 관계에서 지정된 외래 키가 있는 행이 이미 존재하는 경우 이런 현상이 발생할 수 있습니다. 이런 경우, 개체 컨텍스트에 추가된 개체를 업데이트하는 데 Refresh를 사용할 수 없습니다. 그 대신 MergeOption을 위해 OverwriteChanges 값이 있는 개체를 다시 로드합니다.

개체 컨텍스트 관리에 대한 자세한 내용은 방법: 개체 컨텍스트의 데이터 동시성 관리(Entity Framework)를 참조하십시오.

낙관적 동시성 대신 트랜잭션을 사용할 수도 있습니다. 자세한 내용은 연결 및 트랜잭션 관리(Entity Framework)를 참조하십시오.

단원 내용

방법: 개체 컨텍스트의 데이터 동시성 관리(Entity Framework)

참고 항목

개념

개체 사용(Entity Framework)
개체 만들기, 추가, 수정 및 삭제(Entity Framework)