Audit trail and Soft Deletes in EF Core
CreatedAt, UpdatedAt, DeletedAt, CreatedBy, …, DeletedBy
I recently had to add audit fields to DB models, and make sure EF handled them automagically.
What I wanted
I wanted a
CreatedAt field that was a
DateTime, and a
CreatedBy field, that contained the
Guid of the user who was responsible for creating the model instance.
I also wanted similar
UpdatedAt , and
UpdatedBy fields. Nothing fancy.
It would also have been nice to be able to soft delete instances, rather than actually deleting them from the DB.
If it’s not clear, a soft delete marks a column like
IsDeletedas a boolean, so it still exists in the DB, but is not visible to the user.
Rather than use a boolean, it made sense to also have a
DeletedAt DateTime field, and a
DeletedBy field mapped to the user’s id.
Here’s how I expected it to work:
The Auditable abstract class
Assuming I had a
Company model like:
Id => Guid
Name => String
I wanted the
Company model to inherit from an
Auditable class, where the base class would have fields like:
CreatedAt => DateTime
CreatedBy => TId
UpdatedAt => DateTime?
UpdatedBy => TId
DeletedAt => DateTime?
DeletedBy => TId
Did you notice that the *By fields have type
That’s because it’s supposed to be a generic type, to match the
Idfield of the
ApplicationUsermodel from ASP.NET Identity can be of any type, including
Also, did you notice that . the
DeletedAt fields have nullable types? That’s because they’re
null till the instance is updated, or deleted.
Mark as Soft Deletable
In my DbContext, I wanted to be able to mark as DbSet as Soft Deletable with:
And enforce that the audit fields were set with the right data, at the right time with an interface as simple as:
Notice we use
Guid? as the
Id type? That’s because the
DeletedBy fields can be null.