Sunday, July 13, 2008

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
        }

    }

}

Sunday, July 13, 2008 10:27:34 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  |