Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Export (0) Print
Expand All

How to: Execute Business Logic When Saving Changes

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. Starting with the .NET Framework version 4 the SaveChanges method is virtual. This means that you can override this method directly instead of subscribing to the SavingChanges event.

The examples in this topic show how to override the SaveChanges method and 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 the Model and Mapping Files

The first example shows how to override the SaveChanges method in the custom object context class. The second example shows how to use OnContextCreated to register a handler for the SavingChanges event in an instance of ObjectContext. The third example shows how to handle this event in a proxy class that derives from ObjectContext. The fourth 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 SaveChanges method is overridden to enable you to validate objects that have an Added or Modified EntityState. This example is based on the custom object context defined in How to: Define a Custom Object Context.

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


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

See Also

Community Additions

ADD
Show:
© 2015 Microsoft