Overriding ObjectContext.SaveChanges

Someone posted a question in the Entity Framework MSDN forum asking for an example of overriding SaveChanges. I thought I had posted one in an early thread but couldn't find it. Nor could I find an obvious example anywhere on the web, so I thought I would put one here on my blog.

There aren't a lot of public events on the classes created from the code generated classes of an Entity Data Model. But you can override the ones that exist.

ObjectContext exposes SavingChanges.

EntityObjects expose PropertyChanged and PropertyChanging and there's a pair of these for each property in an entity. E.g. for Customer, you'll get OnCustomerIDChanged and OnCustomerIDChanging.

Because these are all partial classes, you need only to create new code files that extend the partial classes. I create a separate code file for each class.

In my sample project which hosts a model for AdventureWorks, therefore, I have a code file named AWEntities (.vb or .cs) that extends the AWEntities class.

Here's an example of that class, which does a few things to my entities when I call SaveChanges before SaveChanges actually pushes the data up to the server. In this sample, I update the ModifiedDate field for Modified Customer entities, then I do the same for new ("Added") Customer Entities as well as create new default password data.

Namespace MyAssemblyNamespace

  Partial Public Class AWEntities

    Private Sub AWEntities_SavingChanges(ByVal sender As Object, ByVal e As System.EventArgs)  _
              Handles Me.SavingChanges

      'find all entities whose state is Modified so that I can 
      '
update the Customer's ModifiedDate property

      Dim changedEntities = _
      Me.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)

      For Each stateEntryEntity In changedEntities
        If TypeOf stateEntryEntity.Entity Is Customer Then
          Dim cust As Customer = cType(stateEntryEntity.Entity,Customer)
          cust.ModifiedDate = Now  'update the ModifiedDate
        End If
      Next

      'make sure new entities get modifiedDate and non-nullable fields populated
      Dim addedEntities = _
      Me.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
      For Each stateEntryEntity In addedEntities
        If TypeOf stateEntryEntity.Entity Is Customer Then
          Dim cust As Customer = cType(stateEntryEntity.Entity,Customer)
          cust.ModifiedDate = Now
          'password hash cannot be null
          'for demo purposes just put a default string in there
          cust.PasswordHash = MyCustomMethodtoHashStrings("
p@ssword")
          'customer table also needs salt for hash
          cust.PasswordSalt = MyCustomMethodtoCreateRandomSaltBytes()
        End If
      Next
    End Sub

  End Class
End Namespace

(7/14: adding the base of the C# partial class as a few people have asked...)

For C#, you need wire up the event handler which you can do when the OnContextCreated event is hit.

namespace MyAssemblyNamespace
{
    public partial class AWEntities
    {
        partial void OnContextCreated()
        {
            this.SavingChanges += new System.EventHandler(mySavingChanges);
        }


        public void mySavingChanges(object sender, System.EventArgs e)
        {
            //saving changes logic
        }

    }

}

#1 Denny on 11.15.2009 at 3:13 PM

this seems to be not working in the current Beta. at least when i tried this today the savingchnages event is never firing in my code.

can you try it?

In debugging i see my event handler is added. but the event never happens.

the data is saved. the rest of the events are happening.

it's like they never raise the event.

my code is in C# and is very much like the msdn sample code.

just no fire of my handler.

#2 Julie on 11.15.2009 at 3:27 PM

@Denny - can you see in debug if the onContextCreated is firing?

#3 Shimmy on 3.23.2010 at 7:38 AM

The event args should of been CancelEventArgs.

For my opinion there should of been many more events, please take a look:

connect.microsoft.com/.../ViewFeedback.as

#4 Rohit Gupta on 3.29.2010 at 12:56 PM

my objectcontext has 100s of entities. I need to do validation of each entity before SaveChanges gets called. So I have a Form for Entity1, I instantiate ObjectContext just for this Form which deals only with Entity1. So to do validation I override the SaveChanges method to do validation on the Entity1 object.

Similarly for 100 other Entities I will create 100 different instances of the ObjectContext and then Override the Savechanges method again for each of the 100 Entities.

Is this the right way to override Savechanges? since it looks like it will call all the other 100 SaveChanges override when just calling SaveChanges just for Entity1 Changes.???

Thanks

#5 Steve Horn on 5.18.2010 at 11:50 AM

I'm on EF v4 using POCO entities, and I am not seeing the partial method "OnContextCreated".

Do you know what I might be doing wrong?

#6 Julie on 5.18.2010 at 11:57 AM

@steve

OnContextCreated is put into your classes by the default EntityObject template.