How to Auto Update Created, Updated and Deleted Timestamps in Entity Framework Core?

How to Auto Update Created, Updated and Deleted Timestamps in Entity Framework Core?

·

3 min read

In many applications, it is useful to track when entities are created, updated, and deleted. Entity Framework Core (EF Core) provides several ways to achieve this, including using database triggers, database default values, and EF Core's built-in features. In this article, we will focus on using EF Core's built-in features to automatically update timestamps when entities are created, updated, and deleted.

Setting up the Entity and DbContext

To begin, let's set up a simple entity class with three timestamp properties: CreatedAt, UpdatedAt, and DeletedAt. We will also add a IsDeleted property to track whether the entity has been deleted or not.

public class MyEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
    public DateTime? DeletedAt { get; set; }
    public bool IsDeleted { get; set; }
}

Next, we will define our DbContext class and configure the MyEntity entity to use the timestamp properties we defined above.

public class MyDbContext : DbContext
{
    public DbSet<MyEntity> MyEntities { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyEntity>()
            .Property(e => e.CreatedAt)
            .HasDefaultValueSql("GETDATE()");

        modelBuilder.Entity<MyEntity>()
            .Property(e => e.UpdatedAt)
            .HasDefaultValueSql("GETDATE()");
    }
}

In the OnModelCreating method, we are using the HasDefaultValueSql method to set the CreatedAt and UpdatedAt properties to the current date and time using the GETDATE() SQL function. This will ensure that these properties are automatically set to the current date and time whenever a new MyEntity is inserted into the database.

Handling Updates and Deletes

Now that we have set up the timestamps for inserts, let's turn our attention to updates and deletes. To handle updates, we can use EF Core's SaveChanges method to update the UpdatedAt property whenever an entity is saved.

Copy codepublic override int SaveChanges()
{
    var modifiedEntities = ChangeTracker.Entries()
        .Where(e => e.State == EntityState.Modified);

    foreach (var entity in modifiedEntities)
    {
        entity.Property("UpdatedAt").CurrentValue = DateTime.Now;
    }

    return base.SaveChanges();
}

We can use a similar approach to handle deletes. However, instead of deleting the entity from the database, we will set the DeletedAt property to the current date and time and set the IsDeleted property to true. This allows us to "soft delete" the entity, meaning it is still present in the database but is no longer considered active.

public override int SaveChanges()
{
    var modifiedEntities = ChangeTracker.Entries()
    .Where(e => e.State == EntityState.Modified);

foreach (var entity in modifiedEntities)
{
    entity.Property("UpdatedAt").CurrentValue = DateTime.Now;
}

var deletedEntities = ChangeTracker.Entries()
    .Where(e => e.State == EntityState.Deleted);

foreach (var entity in deletedEntities)
{
    entity.State = EntityState.Modified;
    entity.Property("DeletedAt").CurrentValue = DateTime.Now;
    entity.Property("IsDeleted").CurrentValue = true;
}

return base.SaveChanges();

Querying Deleted Entities

Now that we have implemented the ability to "soft delete" entities, we may want to include deleted entities in our queries by default. To do this, we can override the OnModelCreating method in our DbContext class and use the QueryFilter method to apply a filter to all queries for the MyEntity type.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<MyEntity>()
        .Property(e => e.CreatedAt)
        .HasDefaultValueSql("GETDATE()");

    modelBuilder.Entity<MyEntity>()
        .Property(e => e.UpdatedAt)
        .HasDefaultValueSql("GETDATE()");

    modelBuilder.Entity<MyEntity>()
        .HasQueryFilter(e => !e.IsDeleted);
}

With this filter in place, all queries for MyEntity will exclude deleted entities by default. If you want to include deleted entities in a specific query, you can use the IgnoreQueryFilters method to override the filter.

var allEntities = context.MyEntities
    .IgnoreQueryFilters()
    .ToList();

In this article, we have seen how to use EF Core's built-in features to automatically update timestamps when entities are created, updated, and deleted. We have also learned how to "soft delete" entities and include them in queries using the QueryFilter and IgnoreQueryFilters methods. By following these techniques, you can easily track the creation, update, and deletion of entities in your EF Core applications.