Procedura: eseguire la logica di business al momento del salvataggio delle modifiche (Entity Framework)

Entity Framework consente di eseguire la logica di business personalizzata prima che le modifiche vengono salvate nel database. L'evento SavingChanges viene generato prima dell'elaborazione di un'operazione SaveChanges. Gestire questo evento per implementare la logica di business personalizzata prima che le modifiche vengono salvate nel database. A partire da .NET Framework versione 4, il metodo SaveChanges è virtual. Pertanto, è possibile eseguire l'override di questo metodo direttamente anziché sottoscrivere l'evento SavingChanges.

Negli esempi inclusi in questo argomento viene mostrato come eseguire l'override del metodo SaveChanges e come gestire l'evento SavingChanges per convalidare gli oggetti modificati in un contesto dell'oggetto prima che le modifiche vengano rese persistenti nel database.

L'esempio incluso in questo argomento è basato sul modello Sales di AdventureWorks. Per eseguire il codice incluso in questo esempio, è necessario avere già aggiunto il modello Sales di AdventureWorks al progetto e avere configurato il progetto per l'utilizzo di Entity Framework . A tale scopo, completare le procedure descritte in Procedura: configurare manualmente un progetto di Entity Framework e Procedura: definire manualmente i file di modello e di mapping (Entity Framework).

Nel primo esempio viene mostrato come eseguire l'override del metodo SaveChanges nella classe del contesto dell'oggetto personalizzata. Nel secondo esempio viene illustrato come utilizzare OnContextCreated per registrare un gestore per l'evento SavingChanges in un'istanza di ObjectContext. Nel terzo esempio viene illustrato come gestire questo evento in una classe proxy che deriva da ObjectContext. Nel quarto esempio viene illustrato il codice che consente di apportare modifiche agli oggetti utilizzando la classe proxy del secondo esempio, quindi viene chiamato SaveChanges.

Esempio

In questo esempio, l'override del metodo SaveChanges viene eseguito per consentire la convalida di oggetti che dispongono di un campo Added o ModifiedEntityState. Questo esempio si basa sul contesto dell'oggetto personalizzato definito in Procedura: definire un contesto dell'oggetto personalizzato (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);
}

In questo esempio il metodo OnContextCreated viene definito come metodo parziale di AdventureWorksEntities. Il gestore per l'evento SavingChanges viene definito in questo metodo parziale. Il gestore eventi consente di verificare che il codice chiamante non abbia aggiunto testo non appropriato nella proprietà SalesOrderHeader.Comment prima che le modifiche possano essere rese persistenti. Se vengono individuati problemi dall'algoritmo di verifica della stringa (non illustrato), viene generata un'eccezione.

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 questo esempio viene utilizzata un'istanza della classe AdventureWorksProxy per modificare la proprietà Comment di un oggetto SalesOrderHeader. Quando viene chiamato SaveChanges sull'istanza dell'oggetto ObjectContext fornita dalla classe AdventureWorksProxy, viene eseguito il codice di convalida dell'esempio precedente.

' 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 questo esempio vengono apportate modifiche agli oggetti nella classe proxy che deriva da ObjectContext, quindi viene chiamato SaveChanges. Questo esempio viene utilizzato per richiamare la gestione degli eventi illustrata nell'esempio precedente.

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

Vedere anche

Attività

Procedura: eseguire la logica di business al momento della modifica dello stato dell'oggetto (Entity Framework)
Procedura: eseguire la logica di business quando vengono modificate le proprietà scalari (Entity Framework)
Procedura: eseguire la logica di business quando vengono modificate le associazioni

Concetti

Utilizzo di oggetti (Entity Framework)