How to: Execute Business Logic When Saving Changes (Entity Framework)

The Entity Framework enables you to execute custom business logic before changes are saved to the database. The SavingChanges event is raised before a SaveChanges operation is processed. Handle this event to implement custom business logic before changes are saved to the database. For more information about how to implement custom business logic, see Customizing Objects (Entity Framework). The examples in this topic show how to handle the SavingChanges event to validate changed objects in an object context before those changes are persisted to the database.

The example in this topic is based on the Adventure Works Sales Model. To run the code in this example, you must have already added the AdventureWorks Sales Model to your project and configured your project to use the Entity Framework. To do this, complete the procedures in How to: Manually Configure an Entity Framework Project and How to: Manually Define an Entity Data Model (Entity Framework).

The first example shows how to use OnContextCreated to register a handler for the SavingChanges event in an instance of ObjectContext. The second example shows how to handle this event in a proxy class that derives from ObjectContext. The third example shows code that makes changes to objects using the proxy class from the second example and then calls SaveChanges.

Example

In this example, the OnContextCreated method is defined as a partial method of AdventureWorksEntities. The handler for the SavingChanges event is defined in this partial method. The event handler verifies that the calling code has not added any inappropriate text in the SalesOrderHeader.Comment property before the changes can be persisted. If the string checking algorithm (not shown) finds any problems, an exception is raised.

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 ApplicationException(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 this example, an instance of the AdventureWorksProxy class is used to change the Comment property of a SalesOrderHeader object. When SaveChanges is called on the instance of ObjectContext provided by the AdventureWorksProxy class, the validation code from the previous example is run.

// Create an instance of the proxy class that returns an object context.
AdventureWorksProxy proxy = new AdventureWorksProxy();

// Get the first order from the context.
SalesOrderHeader order = proxy.Context.SalesOrderHeader.First();

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

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

This example makes changes to objects in the proxy class that derives from ObjectContext and then calls SaveChanges. This example is used to invoke the event handling demonstrated in the previous example.

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

See Also

Community Additions

Show: