I want to have basically on most of my tables a "Last Updated" column so that I can quickly check when it was last updated.
Right now I been just putting a datetime and every time I do an action in my C# code I will save the new DateTime to update that field. Of course this can lead to me forgetting to do this.
I want something more automatic. I don't need this really for auditing purposes so it does not need to be too advanced.
I tried
builder.Property(x => x.RowVersion).IsRowVersion();
what should make a timestamp but when I look at in the database I see 0x00000000000007D1
when I thought it would look more like a datetime.
edit
public class CompanyConfig : IEntityTypeConfiguration<Company>
{
public void Configure(EntityTypeBuilder<Company> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.Property<DateTime>("LastUpdated");
}
}
Assuming you have defined a shadow property LastModified
for each entity you want that functionality as in your example
builder.Property<DateTime>("LastUpdated");
and the question is how to update it automatically.
The technique described in the David's answer is outdated. It's still possible to override SaveChanges
(and SaveChangesAsync
), but there is IMHO a better approach, shown in Populate Created and LastModified automagically in EF Core. It's interface based approach, but can easily be adjusted for shadow property (or any property - the methods used work for both shadow and explicit properties).
Quick recap: Starting with v2.1, EF Core provides State change events:
New
Tracked
AndStateChanged
events onChangeTracker
can be used to write logic that reacts to entities entering theDbContext
or changing their state.
Here is how you can utilize them. Add the following to your derived context class:
void SubscribeStateChangeEvents()
{
ChangeTracker.Tracked += OnEntityTracked;
ChangeTracker.StateChanged += OnEntityStateChanged;
}
void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e)
{
ProcessLastModified(e.Entry);
}
void OnEntityTracked(object sender, EntityTrackedEventArgs e)
{
if (!e.FromQuery)
ProcessLastModified(e.Entry);
}
void ProcessLastModified(EntityEntry entry)
{
if (entry.State == EntityState.Modified || entry.State == EntityState.Added)
{
var property = entry.Metadata.FindProperty("LastModified");
if (property != null && property.ClrType == typeof(DateTime))
entry.CurrentValues[property] = DateTime.UtcNow;
}
}
and add
SubscribeStateChangeEvents();
to your derived context constructor(s).
And that's all. Once the context is subscribed to these events, it will be notified anytime the entity is initially tracked or its state changes. You are interested only with Modified
and Added
state not triggered by query materialization (if you don't need Added
, then just exclude it from the if
condition). Then you check if the entity contains DateTime LastModified
property using the EF Core metadata services and if yes, simply update it with the current date/time.
The pattern in EF Core for this is to make the "LastUpdated" a Shadow Property, and update it during SaveChanges().
See, eg: Implementing Common Audit Fields with EF Core’s Shadow Property
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With