Matthew Jones

We've been searching for a way to audit changes made to our database, and one method we found was doing so via Entity Framework change tracking. Here's how to do it, what's required to set it up, and some drawbacks of this method.

Let's get started!

Background

Entity Framework tracks changes made to entities when those entities are loaded from the Context. So, if we load an entity and modify it, like so:

When the call to SaveChanges is made, Entity Framework is aware of what values have changed, what the new values are, and what the original values are. We need to access this information to accomplish the change tracking we want.

Setup

For this demo, let's assume we have the following entities and context:

Notice the property PrimaryKeyValue. Part of recording "which entity changed" is both which table changed (e.g. "Movie", "Actor", etc) and which specific record in that table changed (e.g. 1, 2, etc.). In our implementation, we will need both of these for proper, thorough auditing. Let's see how we can actually implement auditing using Entity Framework.

In order to get all the data we need to produce these change logs, we will follow five steps:

Override the Context's SaveChanges Method

Get the Change Details

Get the Primary Key Values

Save the Logs

Let's see what we need to accomplish, step-by-step.

Step 1: Override the Context's SaveChanges Method

public override int SaveChanges()
{
return base.SaveChanges();
}

This allows us to automatically generate change logs any time an entity is saved to our database.

Step 2: Get the Change Details

How can we get the actual changes that will occur for this execution of SaveChanges? We will use the ChangeTracker property of DbContext, like so:

So what is DbEntityEntry? That type represents the change that will be made to a particular entity, including the original and current values and the state (Added, Modified, Deleted, etc) of that entity. What we now have is a list of all the entities being modified by this context.

Using this list of changes, we can cycle through each change and determine what values are actually changing:

In order to retrieve the primary keys, we must cast our DbContext down to IObjectContextAdapter and query the ObjectStateManager. Once we have access to that manager, we can get the primary key value (note that this method assumes a single-column primary key, which is not necessarily a good real-world scenario, see Drawbacks).

Step 4: Save the Logs

All we have to do now is add the logs to the Context and save the changes.

Drawbacks

There are a couple of significant drawbacks to this particular solution:

No auditing for Added entities. This is because, in my system, the database is responsible for creating the primary key values (via IDENTITY columns) and therefore the primary keys do not exist before the entity is added to the database. Attempting to use the database-generated primary keys for Added entities would result in two round-trips to the database on every save.

Support for single-column primary keys only. This code makes an explicit assumption that only one column per table in your database is the primary key, which is not true in the real world.

Summary

The method outlined about is a good way to track and audit changes made to existing entities. It allows you to record the entity changed, the value changed, the primary key of the changed record, and the date of change. However, it does not track the adding of entities and it does not support multiple-column primary keys. With all of that said, I feel this is a pretty good step toward having full auditing in Entity Framework for our system.

Did I miss something, or can you improve my code? Please feel free to do so, and share in the comments!