Entity Framework 6: audit/track changes
Addition to the answer from @AlaaMasoud.
With CreatedDate
and UpdatedDate
:
interface IEntityDate
{
DateTime CreatedDate { get; set; }
DateTime UpdatedDate { get; set; }
}
public abstract class EntityBase<T1>: IEntityDate
{
public T1 Id { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual string CreatedBy { get; set; }
public virtual DateTime UpdatedDate { get; set; }
public virtual string UpdatedBy { get; set; }
}
public override int SaveChanges()
{
var now = DateTime.Now;
foreach (var changedEntity in ChangeTracker.Entries())
{
if (changedEntity.Entity is IEntityDate entity)
{
switch (changedEntity.State)
{
case EntityState.Added:
entity.CreatedDate = now;
entity.UpdatedDate = now;
break;
case EntityState.Modified:
Entry(entity).Property(x => x.CreatedDate).IsModified = false;
entity.UpdatedDate = now;
break;
}
}
}
return base.SaveChanges();
}
To handle CreatedBy
and UpdatedBy
I use a wrapper for DbContext
like this:
public interface IEntity
{
DateTime CreatedDate { get; set; }
string CreatedBy { get; set; }
DateTime UpdatedDate { get; set; }
string UpdatedBy { get; set; }
}
public interface ICurrentUser
{
string GetUsername();
}
public class ApplicationDbContextUserWrapper
{
public ApplicationDbContext Context;
public ApplicationDbContextUserWrapper(ApplicationDbContext context, ICurrentUser currentUser)
{
context.CurrentUser = currentUser;
this.Context = context;
}
}
public class ApplicationDbContext : DbContext
{
public ICurrentUser CurrentUser;
public override int SaveChanges()
{
var now = DateTime.Now;
foreach (var changedEntity in ChangeTracker.Entries())
{
if (changedEntity.Entity is IEntity entity)
{
switch (changedEntity.State)
{
case EntityState.Added:
entity.CreatedDate = now;
entity.UpdatedDate = now;
entity.CreatedBy = CurrentUser.GetUsername();
entity.UpdatedBy = CurrentUser.GetUsername();
break;
case EntityState.Modified:
Entry(entity).Property(x => x.CreatedBy).IsModified = false;
Entry(entity).Property(x => x.CreatedDate).IsModified = false;
entity.UpdatedDate = now;
entity.UpdatedBy = CurrentUser.GetUsername();
break;
}
}
}
return base.SaveChanges();
}
...
Source:
https://stackoverflow.com/a/53669556/3850405
There is one way to do it: you can create a partial class that is the same name as your object context and implement an override of the SaveChanges
method. In this override you can look at all the changes that will be pushed to the DB and process them.
You can process them any way you like, in the following example I created an interface IAutoTimestampEntity
that contained a creation date and a modification date. Any object of this type would be automatically updated with the time of change.
public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
var timestamp = DateTime.Now;
foreach (var InsertedAutoTimestampEntity in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added).Select(ose => ose.Entity).OfType<IAutoTimestampEntity>())
{
InsertedAutoTimestampEntity.CreationDate = timestamp;
InsertedAutoTimestampEntity.ModificationDate = timestamp;
}
foreach (var UpdatedAutoTimestampEntity in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified).Select(ose => ose.Entity).OfType<IAutoTimestampEntity>())
{
UpdatedAutoTimestampEntity.ModificationDate = timestamp;
}
return base.SaveChanges(options);
}
You can use the same principle, or you can look at the type of each changed entity in details. I like the declarative aspect of the interface though. It lets you expose one aspect of automation explicitly instead of letting it be done silently by the EF layer.
If you have a DbContext
instead of an ObjectContext
, cast your DbContext
to IObjectContextAdapter
to access the ObjectStateManager
If using EF6's DbContext
you can use ChangeTracker
in SaveChanges
override to find added/modified entities of custom type, for example IAuditedEntity.
public interface IAuditedEntity {
string CreatedBy { get; set; }
DateTime CreatedAt { get; set; }
string LastModifiedBy { get; set; }
DateTime LastModifiedAt { get; set; }
}
public override int SaveChanges() {
var addedAuditedEntities = ChangeTracker.Entries<IAuditedEntity>()
.Where(p => p.State == EntityState.Added)
.Select(p => p.Entity);
var modifiedAuditedEntities = ChangeTracker.Entries<IAuditedEntity>()
.Where(p => p.State == EntityState.Modified)
.Select(p => p.Entity);
var now = DateTime.UtcNow;
foreach (var added in addedAuditedEntities) {
added.CreatedAt = now;
added.LastModifiedAt = now;
}
foreach (var modified in modifiedAuditedEntities) {
modified.LastModifiedAt = now;
}
return base.SaveChanges();
}