Gewusst wie: Ausführen von Geschäftslogik beim Speichern von Änderungen (Entity Framework)

Mithilfe von Entity Framework kann benutzerdefinierte Geschäftslogik ausgeführt werden, bevor Änderungen in der Datenbank gespeichert werden. Das SavingChanges-Ereignis wird vor der Ausführung einer SaveChanges-Operation ausgelöst. Behandeln Sie dieses Ereignis, um benutzerdefinierte Geschäftslogik zu implementieren, bevor Änderungen in der Datenbank gespeichert werden. Die SaveChanges-Methode ist ab .NET Framework, Version 4, virtual. Dies bedeutet, dass Sie diese Methode direkt überschreiben können, statt das SavingChanges-Ereignis zu abonnieren.

Die Beispiele in diesem Thema zeigen, wie die SaveChanges-Methode überschrieben und das SavingChanges-Ereignis behandelt wird, um geänderte Objekte im Objektkontext zu überprüfen, bevor diese Änderungen in der Datenbank gespeichert werden.

Das Beispiel in diesem Thema beruht auf dem AdventureWorks Sales-Modell. Zum Ausführen des Codes in diesem Beispiel müssen Sie Ihrem Projekt bereits das AdventureWorks Sales-Modell hinzugefügt und das Projekt so konfiguriert haben, dass Entity Framework verwendet wird. Verwenden Sie dazu die Verfahren aus Gewusst wie: Manuelles Konfigurieren eines Entity Framework-Projekts und Gewusst wie: Manuelles Definieren der Modell- und Zuordnungsdateien (Entity Framework).

Im ersten Beispiel wird gezeigt, wie die SaveChanges-Methode in der benutzerdefinierten Objektkontextklasse überschrieben wird. Im zweiten Beispiel wird gezeigt, wie OnContextCreated verwendet wird, um einen Handler für das SavingChanges-Ereignis in einer Instanz von ObjectContext zu registrieren. Im dritten Beispiel wird gezeigt, wie dieses Ereignis in einer Proxyklasse, die von ObjectContext abgeleitet ist, behandelt wird. Das vierte Beispiel zeigt Code, der mit der Proxyklasse aus dem zweiten Beispiel Änderungen an Objekten vornimmt und anschließend SaveChanges aufruft.

Beispiel

In diesem Beispiel wird die SaveChanges-Methode überschrieben, damit Sie Objekte überprüfen können, die über den Zustand Added oder ModifiedEntityState verfügen. Dieses Beispiel basiert auf dem benutzerdefinierten Objektkontext, der in Gewusst wie: Definieren eines benutzerdefinierten Objektkontexts (Entity Framework) definiert wird.

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);
}

In diesem Beispiel wird die OnContextCreated-Methode als partielle Methode von AdventureWorksEntities definiert. Der Handler für das SavingChanges-Ereignis wird in dieser partiellen Methode definiert. Der Ereignishandler stellt sicher, dass der aufrufende Code keinen unzulässigen Text in der SalesOrderHeader.Comment-Eigenschaft enthält, bevor die Änderungen gespeichert werden. Wenn der Algorithmus zum Überprüfen der Zeichenfolge (nicht dargestellt) Probleme findet, wird eine Ausnahme ausgelöst.

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"));
                }
            }
        }
    }
}

In diesem Beispiel wird eine Instanz der AdventureWorksProxy-Klasse verwendet, um die Comment-Eigenschaft eines SalesOrderHeader-Objekts zu ändern. Wird SaveChanges für die von der AdventureWorksProxy-Klasse bereitgestellte Instanz von ObjectContext aufgerufen, wird der Validierungscode aus dem vorherigen Beispiel ausgeführt.

' 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());
}

In diesem Beispiel werden Änderungen an Objekten in der Proxyklasse vorgenommen, die von ObjectContext stammt und anschließend SaveChanges aufruft. Dieses Beispiel wird verwendet, um die im vorherigen Beispiel gezeigte Ereignisbehandlung aufzurufen.

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"));
                    }
                }
            }
        }
    }
}

Siehe auch

Aufgaben

Gewusst wie: Ausführen von Geschäftslogik, wenn sich der Objektzustand ändert
Gewusst wie: Ausführen von Geschäftslogik im Verlauf von Skalareigenschaftsänderungen (Entity Framework)
Gewusst wie: Ausführen von Geschäftslogik im Verlauf von Zuordnungsänderungen

Konzepte

Arbeiten mit Objekten (Entity Framework)