Matthew Chestnut is a Senior Consultant at ThreeWill. He has over 25 years of software development experience around enterprise and departmental business productivity applications. He has a proven track record of quality software development, on-budget project management and management of successful software development teams.
First Step on How to Auto Generate Created / Updated Field in EF Core
On a recent Entity Framework (EF) Core project we implemented a common pattern used to set property values for our entities / database objects. In this case, to auto generate created / updated field in EF Core.
Entity Framework Core is used by .NET developers to work with a database using .NET objects. It is an Object Relational Mapper (ORM) which uses tables and columns in a relational database to simplify mapping between objects. In a ThreeWill podcast episode, we explain using ASP.NET vs Sharepoint on projects, for those interested in the difference.
In the example provided below, I am using EF Core to set the data values to indicate when the entity was created and last updated. This way, I do not have to worry about setting those values manually.
Three Key Points
Key point #1 – Create a common class from which your models can inherit from, e.g. BaseEntity:
public class BaseEntity { public DateTime CreatedOn { get; set; }; public DateTime UpdatedOn { get; set; }; }
Key point #2 – Inherit from the common class where appropriate:
public class Company : BaseEntity { public int id { get; set; } public string name { get; set; } } public class Person : BaseEntity { public int id { get; set; } public string name { get; set; } }
Key point #3 – In your EF Core DbContext class, override the SaveChanges and SaveChangesAsync classes and implement a common method to inspect and set the property values, e.g. OnBeforeSaving:
public class MyApplicationDataContext : DbContext { public override int SaveChanges(bool acceptAllChangesOnSuccess) { OnBeforeSaving(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override async Task<int> SaveChangesAsync( bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken) ) { OnBeforeSaving(); return (await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken)); } private void OnBeforeSaving() { var entries = ChangeTracker.Entries(); var utcNow = DateTime.UtcNow; foreach (var entry in entries) { // for entities that inherit from BaseEntity, // set UpdatedOn / CreatedOn appropriately if (entry.Entity is BaseEntity trackable) { switch (entry.State) { case EntityState.Modified: // set the updated date to "now" trackable.UpdatedOn = utcNow; // mark property as "don't touch" // we don't want to update on a Modify operation entry.Property("CreatedOn").IsModified = false; break; case EntityState.Added: // set both updated and created date to "now" trackable.CreatedOn = utcNow; trackable.UpdatedOn = utcNow; break; } } } } }
If the entity inherits from BaseEntity the code will update the appropriate date/time value when the object is created and/or updated.
Pretty cool, huh?
Comment below if this has helped you in any way!
5 Comments
Mehmet Yagci
entry.Property("CreatedOn").IsModified = false;
Thanks for that
Warren Buckley
Im struggling to understand what the purpose is of this line:
entry.Property("CreatedOn").IsModified = false;
What is that supposed to prevent?
Aside from that, thanks for the post.
Matthew Chestnut
Thank you for your comment, Warren!
By setting the "IsModified" attribute to false for the Created On date for an *update* of a item, I'm just guaranteeing that the created on date is not changed when an item is updated. But, if an item is added, I do want that value updated. Even if the developer explicitly sets the created date to another value on an update (whether on purpose or by accident) this base class code prevents the update from happening.
By setting the attribute to false, a SQL update is not generated for this field.
Dan
One simple improvement is to use `nameof(trackable.CreatedOn)` instead of the literal `"CreatedOn"` where you set `IsModified`.
Matthew Chestnut
Thank you for the great suggestion, Dan!