Share via


Cómo: Ejecutar la lógica de negocios al guardar los cambios (Entity Framework)

Entity Framework le permite ejecutar la lógica de negocios personalizada antes de guardar cambios en la base de datos. Antes de que se procese la operación SaveChanges, se desencadena el evento SavingChanges. Controle este evento para implementar la lógica de negocios personalizada antes de que se guarden los cambios en la base de datos. A partir de la versión 4 de .NET Framework el método SaveChanges es virtual. Esto significa que puede reemplazar directamente este método en lugar de suscribirse al evento SavingChanges.

En los ejemplos de este tema, se muestra cómo reemplazar el método SaveChanges y controlar el evento SavingChanges para validar los objetos modificados en un contexto de objetos antes de que se guarden los cambios en la base de datos.

El ejemplo de este tema se basa en el modelo Adventure Works Sales. Para ejecutar el código de este ejemplo, debe haber agregado el modelo AdventureWorks Sales al proyecto y haber configurado el proyecto para que use Entity Framework . Para ello, complete los procedimientos de Cómo configurar manualmente un proyecto de Entity Framework y Cómo: Definir manualmente los archivos de asignación y modelo (Entity Framework).

El primer ejemplo muestra cómo reemplazar el método SaveChanges en la clase de contexto de objetos personalizada. En el segundo ejemplo se muestra cómo utilizar OnContextCreated para registrar un controlador para el evento SavingChanges en una instancia de ObjectContext. En el tercer ejemplo se muestra cómo controlar este evento en una clase proxy que se deriva de ObjectContext. En el cuarto ejemplo se muestra código que realiza cambios en objetos utilizando la clase proxy del segundo ejemplo y, a continuación, llama a SaveChanges.

Ejemplo

En este ejemplo, el método SaveChanges se reemplaza para permitir validar objetos que presentan un valor Added o Modified en EntityState. Este ejemplo está basado en el contexto de objetos personalizado definido en Cómo: Definir un contexto del objeto personalizado (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);
}

En este ejemplo, el método OnContextCreated se define como un método parcial de AdventureWorksEntities. El controlador para el evento SavingChanges se define en este método parcial. El controlador de eventos comprueba que el código de llamada no ha agregado texto inadecuado a la propiedad SalesOrderHeader.Comment antes de que se guarden los cambios. Si la cadena que comprueba el algoritmo (no se muestra) encuentra cualquier problema, se produce una excepción.

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

En este ejemplo, se usa una instancia de AdventureWorksProxy para cambiar la propiedad Comment de un objeto SalesOrderHeader. Cuando se llama a SaveChanges en la instancia de ObjectContext que proporciona la clase AdventureWorksProxy, se ejecuta el código de validación del ejemplo anterior.

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

En este ejemplo se realizan cambios en objetos en la clase de proxy que deriva de ObjectContext y, a continuación, se llama a SaveChanges. Este ejemplo se utiliza para invocar el control de eventos que se muestra en el ejemplo anterior.

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

Vea también

Tareas

Cómo: Ejecutar la lógica de negocios cuando el estado del objeto cambia
Cómo: Ejecutar la lógica empresarial durante los cambios de propiedades escalares (Entity Framework)
Cómo: Ejecutar la lógica de negocios durante los cambios de asociación

Conceptos

Trabajar con objetos (Entity Framework)