Basically, you have two ways to achieve this:
Using ChangeTracker API (EF 6+):
So we are currently doing this in EF 6, and it is still valid and works for EF 7:
First you need to make sure that your objects implement a common interface for validation fields:
public interface IAuditableEntity { int? CreatedById { get; set; } DateTime Created { get; set; } int? ModifiedById { get; set; } DateTime Modified { get; set; } }
You can then override SaveChanges and update each common field using audit values:
public override int SaveChanges() { int? userId = null; if (System.Web.HttpContext.Current != null) userId = (from user in Users.Where(u => u.UserName == System.Web.HttpContext.Current.User.Identity.Name) select user.Id).SingleOrDefault(); var modifiedEntries = ChangeTracker.Entries<IAuditableEntity>() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified); foreach (EntityEntry<IAuditableEntity> entry in modifiedEntries) { entry.Entity.ModifiedById = UserId; entry.Entity.Modified = DateTime.Now; if (entry.State == EntityState.Added) { entry.Entity.CreatedById = UserId; entry.Entity.Created = DateTime.Now; } } return base.SaveChanges(); }
Using EF 7's new Shadow Properties:
Shadow properties are properties that do not exist in your entity class. The value and state of these properties is supported exclusively in Track Track.
In other words, audit columns will not appear on your objects, which appear to be a better option, comparable to the one above, where you should include them in your objects.
To implement shadow properties, you must first configure them on your objects. Say, for example, you have a User object that must have several audit columns:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>().Property<int>("CreatedById"); modelBuilder.Entity<User>().Property<DateTime>("Created"); modelBuilder.Entity<User>().Property<int>("ModifiedById"); modelBuilder.Entity<User>().Property<DateTime>("Modified"); }
After setting up, now you can access them on SaveChanges () and update their values accordingly:
public override int SaveChanges() { int? userId = null; if (System.Web.HttpContext.Current != null) userId = (from user in Users.Where(u => u.UserName == System.Web.HttpContext.Current.User.Identity.Name) select user.Id).SingleOrDefault(); var modifiedBidEntries = ChangeTracker.Entries<User>() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified); foreach (EntityEntry<User> entry in modifiedBidEntries) { entry.Property("Modified").CurrentValue = DateTime.UtcNow; entry.Property("ModifiedById").CurrentValue = userId; if (entry.State == EntityState.Added) { entry.Property("Created").CurrentValue = DateTime.UtcNow; entry.Property("CreatedById").CurrentValue = userId; } } return base.SaveChanges(); }
Final thoughts:
To implement something like audit columns, I will take the Shadow Properties approach, because these are problems with cross-sections and do not necessarily belong to my domain objects, so if they are implemented this way, my domain objects will be nice and clean.