Caffeine-Powered Life

Easy Audit Information in Entity Framework

Another pretty common problem – we have a lot of tables that have the same 4 columns to track simple audit information. I’m sure you’ve all seend something like this before.

  CreatedAt    DATETIME NULL,

  CreatedBy    VARCHAR(256) NULL,

  UpdatedAt    DATETIME NULL,

  UpdatedBy    VARCHAR(256) NULL

When inserting or updating records, we really don’t want to mess around with updating all of this stuff all of the time, especially if you have a lot of models in your application. It’s just a pain, and it violates DRY. So let’s fix that.

The first thing we need is an interface. We’ll implement this interface on all of our models that have this audit tracking information.

public interface IAuditable

{

  DateTime? CreatedAt { get; set; }

  string CreatedBy { get; set; }

  DateTime? UpdatedAt { get; set; }

  string UpdatedBy { get; set; }

}

Now we need to look to our DbContext for further insights. (I’m using the DbContext code generator, available as a Visual Studio Extension.) We see that our entity context extends the DbContext, and it’s created as a partial class by default. Let’s take advantage of this and override the SaveChanges method.

partial class MyContext

{

  public override int SaveChanges()

  {

    UpdateAuditInformation();

    return base.SaveChanges();

  }



  private IEnumerable ChangeTrackerEntries

  {

    get { return this.ChangeTracker.Entries(); }

  }



  private void UpdateAuditInformation()

  {

    UpdatedAddedEntries();

    UpdatedModifiedEntries();

  }



  private void UpdateAddedEntries()

  {

    var addedEntries = ChangeTrackerEntries.Where(e => e.State == EntityState.Added && e.Entity is IAuditable).Select(e => e.Entity);

    foreach (var addedEntry in modifiedEntries)    

    {

      addedEntry.CreatedAt = DateTime.UtcNow;

      addedEntry.CreatedBy = HttpSession.Current.UserName;

      addedEntry.UpdatedAt = addedEntry.CreatedAt;

      addedEntry.UpdatedBy = addedEntry.CreatedBy;

    }

  } 



  private void UpdateModifiedEntries()

  {

    var modifiedEntries = ChangeTrackerEntries.Where(e => e.State == EntityState.Modified && e.Entity is IAuditable).Select(e => e.Entity);

    foreach (var modifiedEntry in modifiedEntries)

    {

      modifiedEntry.UpdatedAt = DateTime.UtcNow;

      modifiedEntry.UpdatedBy = HttpSession.Current.UserName;

    }

  }

}

The HttpSession.Current is just a strongly typed wrapper for an HTTP session, specific to this application. There’s nothing special, so feel free to use any method available to get the current application user. For example, if you’re using FormsAuthentication or WindowsAuthentication, you can simply call HttpContext.Current.User.

Hope you like it!

Comments