방법: 변경된 내용을 저장할 때 비즈니스 논리 실행(Entity Framework)

Entity Framework 를 사용하면 변경 내용을 데이터베이스에 저장하기 전에 사용자 지정 비즈니스 논리를 실행할 수 있습니다. SavingChanges 이벤트는 SaveChanges 작업이 처리되기 전에 발생합니다. 변경 내용을 데이터베이스에 저장하기 전에 사용자 지정 비즈니스 논리를 구현하는 이 이벤트를 처리하십시오. .NET Framework 버전 4부터 SaveChanges 메서드는 virtual입니다. 따라서 SavingChanges 이벤트를 구독하는 대신 이 메서드를 직접 재정의할 수 있습니다.

이 항목의 예제에서는 개체 컨텍스트에서 변경된 개체를 데이터베이스에 유지하기 전에 이러한 변경 내용의 유효성을 검사하기 위해 SaveChanges 메서드를 재정의하고 SavingChanges 이벤트를 처리하는 방법을 보여 줍니다.

이 항목의 예제는 Adventure Works Sales 모델을 기반으로 합니다. 이 예제의 코드를 실행하려면 프로젝트에 AdventureWorks Sales 모델을 추가하고 Entity Framework 를 사용하도록 프로젝트를 구성해야 합니다. 이렇게 하려면 방법: Entity Framework 프로젝트 수동 구성방법: 수동으로 모델 및 매핑 파일 정의(Entity Framework)의 절차를 수행합니다.

첫 번째 예제에서는 사용자 지정 개체 컨텍스트 클래스에서 SaveChanges 메서드를 재정의하는 방법을 보여 줍니다. 두 번째 예제에서는 OnContextCreated를 사용하여 ObjectContext의 인스턴스에서 SavingChanges 이벤트의 처리기를 등록하는 방법을 보여 줍니다. 세 번째 예제에서는 ObjectContext에서 파생되는 프록시 클래스에서 이 이벤트를 처리하는 방법을 보여 줍니다. 네 번째 예제에서는 두 번째 예제의 프록시 클래스를 사용하는 개체를 변경한 다음 SaveChanges를 호출하는 코드를 보여 줍니다.

예제

이 예제에서는 EntityStateAdded 또는 Modified인 개체의 유효성을 검사할 수 있도록 SaveChanges 메서드를 재정의합니다. 이 예제는 방법: 사용자 지정 개체 컨텍스트 정의(Entity Framework)에서 정의한 사용자 지정 개체 컨텍스트를 기반으로 합니다.

Public Overloads Overrides Function SaveChanges(ByVal options As SaveOptions) As Integer

    For Each entry As ObjectStateEntry In ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
        ' Validate the objects in the Added and Modified state 
        ' if the validation fails throw an exeption. 
    Next
    Return MyBase.SaveChanges(options)
End Function
public override int SaveChanges(SaveOptions options)
{

    foreach (ObjectStateEntry entry in
        ObjectStateManager.GetObjectStateEntries(
        EntityState.Added | EntityState.Modified))
    {
        // Validate the objects in the Added and Modified state
        // if the validation fails throw an exeption.
    }
    return base.SaveChanges(options);
}

이 예제에서 OnContextCreated 메서드는 AdventureWorksEntities 메서드의 부분 메서드(Partial Method)로 정의됩니다. SavingChanges 이벤트를 담당하는 처리기는 이 부분 메서드(Partial Method)에서 정의됩니다. 이 이벤트 처리기는 변경 내용을 유지하기 전에 호출 코드에서 부적합한 텍스트를 SalesOrderHeader.Comment 속성에 추가하지 않았는지 확인합니다. 문자열 확인 알고리즘에서 문제를 찾은 경우(표시되지 않음) 예외가 발생합니다.

Partial Public Class AdventureWorksEntities
    Private Sub OnContextCreated()
        ' Register the handler for the SavingChanges event. 
        AddHandler Me.SavingChanges, AddressOf context_SavingChanges
    End Sub
    ' SavingChanges event handler. 
    Private Shared Sub context_SavingChanges(ByVal sender As Object, ByVal e As EventArgs)
        ' Validate the state of each entity in the context 
        ' before SaveChanges can succeed. 
        For Each entry As ObjectStateEntry In DirectCast(sender, ObjectContext).ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
            ' Find an object state entry for a SalesOrderHeader object. 
            If Not entry.IsRelationship AndAlso (entry.Entity.GetType() Is GetType(SalesOrderHeader)) Then
                Dim orderToCheck As SalesOrderHeader = TryCast(entry.Entity, SalesOrderHeader)

                ' Call a helper method that performs string checking 
                ' on the Comment property. 
                Dim textNotAllowed As String = Validator.CheckStringForLanguage(orderToCheck.Comment)

                ' If the validation method returns a problem string, raise an error. 
                If textNotAllowed <> String.Empty Then
                    Throw New ArgumentException(String.Format("Changes cannot be " & _
                                                                "saved because the {0} '{1}' object contains a " & _
                                                                "string that is not allowed in the property '{2}'.", _
                                                                entry.State, "SalesOrderHeader", "Comment"))
                End If
            End If
        Next
    End Sub
End Class
public partial class AdventureWorksEntities
{
    partial void OnContextCreated()
    {
        // Register the handler for the SavingChanges event.
        this.SavingChanges
            += new EventHandler(context_SavingChanges);
    }
    // SavingChanges event handler.
    private static void context_SavingChanges(object sender, EventArgs e)
    {
        // Validate the state of each entity in the context
        // before SaveChanges can succeed.
        foreach (ObjectStateEntry entry in
            ((ObjectContext)sender).ObjectStateManager.GetObjectStateEntries(
            EntityState.Added | EntityState.Modified))
        {
            // Find an object state entry for a SalesOrderHeader object. 
            if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(SalesOrderHeader)))
            {
                SalesOrderHeader orderToCheck = entry.Entity as SalesOrderHeader;

                // Call a helper method that performs string checking 
                // on the Comment property.
                string textNotAllowed = Validator.CheckStringForLanguage(
                    orderToCheck.Comment);

                // If the validation method returns a problem string, raise an error.
                if (textNotAllowed != string.Empty)
                {
                    throw new ArgumentException(String.Format("Changes cannot be "
                        + "saved because the {0} '{1}' object contains a "
                        + "string that is not allowed in the property '{2}'.",
                        entry.State, "SalesOrderHeader", "Comment"));
                }
            }
        }
    }
}

이 예제에서 AdventureWorksProxy 클래스의 인스턴스는 SalesOrderHeader 개체의 Comment 속성을 변경하는 데 사용됩니다. SaveChangesAdventureWorksProxy 클래스에서 제공된 ObjectContext의 인스턴스에서 호출되면 이전 예제의 유효성 검사 코드가 실행됩니다.

' Create an instance of the proxy class that returns an object context. 
Dim context As New AdventureWorksProxy()
' Get the first order from the context. 
Dim order As SalesOrderHeader = context.Context.SalesOrderHeaders.First()

' Add some text that we want to catch before saving changes. 
order.Comment = "some text"

Try
    ' Save changes using the proxy class. 
    Dim changes As Integer = context.Context.SaveChanges()
Catch ex As InvalidOperationException
    ' Handle the exception returned by the proxy class 
    ' validation if a problem string is found. 
    Console.WriteLine(ex.ToString())
// Create an instance of the proxy class that returns an object context.
AdventureWorksProxy context = new AdventureWorksProxy();
// Get the first order from the context.
SalesOrderHeader order =
    context.Context.SalesOrderHeaders.First();

// Add some text that we want to catch before saving changes.
order.Comment = "some text";

try
{
    // Save changes using the proxy class.
    int changes = context.Context.SaveChanges();
}
catch (InvalidOperationException ex)
{
    // Handle the exception returned by the proxy class
    // validation if a problem string is found.
    Console.WriteLine(ex.ToString());
}

이 예제에서는 ObjectContext에서 파생되는 프록시 클래스에서 개체를 변경한 다음 SaveChanges를 호출합니다. 이 예제는 이전 예제에 제시된 이벤트 처리를 호출하는 데 사용됩니다.

Public Class AdventureWorksProxy
    ' Define the object context to be provided. 
    Private contextProxy As New AdventureWorksEntities()

    Public Sub New()
        ' When the object is initialized, register the 
        ' handler for the SavingChanges event. 
        AddHandler contextProxy.SavingChanges, AddressOf context_SavingChanges
    End Sub

    ' Method that provides an object context. 
    Public ReadOnly Property Context() As AdventureWorksEntities
        Get
            Return contextProxy
        End Get
    End Property

    ' SavingChanges event handler. 
    Private Sub context_SavingChanges(ByVal sender As Object, ByVal e As EventArgs)
        ' Ensure that we are passed an ObjectContext 
        Dim context As ObjectContext = TryCast(sender, ObjectContext)
        If context IsNot Nothing Then

            ' Validate the state of each entity in the context 
            ' before SaveChanges can succeed. 
            For Each entry As ObjectStateEntry In context.ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
                ' Find an object state entry for a SalesOrderHeader object. 
                If Not entry.IsRelationship AndAlso (entry.Entity.GetType() Is GetType(SalesOrderHeader)) Then
                    Dim orderToCheck As SalesOrderHeader = TryCast(entry.Entity, SalesOrderHeader)

                    ' Call a helper method that performs string checking 
                    ' on the Comment property. 
                    Dim textNotAllowed As String = Validator.CheckStringForLanguage(orderToCheck.Comment)

                    ' If the validation method returns a problem string, raise an error. 
                    If textNotAllowed <> String.Empty Then
                        Throw New ArgumentException(String.Format("Changes cannot be " & _
                                                                    "saved because the {0} '{1}' object contains a " & _
                                                                    "string that is not allowed in the property '{2}'.", _
                                                                    entry.State, "SalesOrderHeader", "Comment"))
                    End If
                End If
            Next
        End If
    End Sub
End Class
public class AdventureWorksProxy
{
    // Define the object context to be provided.
    private AdventureWorksEntities contextProxy =
        new AdventureWorksEntities();

    public AdventureWorksProxy()
    {
        // When the object is initialized, register the 
        // handler for the SavingChanges event.
        contextProxy.SavingChanges
            += new EventHandler(context_SavingChanges);
    }

    // Method that provides an object context.
    public AdventureWorksEntities Context
    {
        get
        {
            return contextProxy;
        }
    }

    // SavingChanges event handler.
    private void context_SavingChanges(object sender, EventArgs e)
    {
        // Ensure that we are passed an ObjectContext
        ObjectContext context = sender as ObjectContext;
        if (context != null)
        {

            // Validate the state of each entity in the context
            // before SaveChanges can succeed.
            foreach (ObjectStateEntry entry in
                context.ObjectStateManager.GetObjectStateEntries(
                EntityState.Added | EntityState.Modified))
            {
                // Find an object state entry for a SalesOrderHeader object. 
                if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(SalesOrderHeader)))
                {
                    SalesOrderHeader orderToCheck = entry.Entity as SalesOrderHeader;

                    // Call a helper method that performs string checking 
                    // on the Comment property.
                    string textNotAllowed = Validator.CheckStringForLanguage(
                        orderToCheck.Comment);

                    // If the validation method returns a problem string, raise an error.
                    if (textNotAllowed != string.Empty)
                    {
                        throw new ArgumentException(String.Format("Changes cannot be "
                            + "saved because the {0} '{1}' object contains a "
                            + "string that is not allowed in the property '{2}'.",
                            entry.State, "SalesOrderHeader", "Comment"));
                    }
                }
            }
        }
    }
}

참고 항목

작업

방법: 개체 상태가 변경될 때 비즈니스 논리 실행(Entity Framework)
방법: 스칼라 속성 변경 중 비즈니스 논리 실행(Entity Framework)
방법: 연결 변경 중 비즈니스 논리 실행

개념

개체 사용(Entity Framework)