8

I have the following entity:

public class User
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string UserAddress { get; set; }
    public DateTime CreateDate { get; set; }
    public DateTime? UpdateDate { get; set; }
}

How can I make the UpdateDate field of the User entity become the server DateTime on an update? That is to say, whenever an entity in the database becomes modified, the UpdateDate field changes to that exact date and time as well as if to say UpdateDate = new DateTime(Datetime.Now.Ticks)

3
  • @Patrick: Hi, I want to assign the server date to UpdateDate field when use context.SaveChanges(), after update the record. Commented Jul 5, 2014 at 23:04
  • See: stackoverflow.com/questions/3879011/… Commented Jul 6, 2014 at 0:37
  • 2
    The only way to do this correctly is by a database trigger. There's no way to tell how much latency there will be between fetching the db date/time and the actual commit of an entity. Commented Jul 6, 2014 at 8:29

4 Answers 4

18

First, consider that DateTimeOffset is a better choice if you are going to be storing local times. Alternatively, you could use a UTC-based DateTime, but I will show DateTimeOffset in this example. Never store a local-based DateTime for this purpose, as you will have issues with time zones and daylight saving time. See also: The Case Against DateTime.Now.

Next, consider a common interface that you can use for entities that you wish to track created/updated values.

public interface IDatedEntity
{
    DateTimeOffset Created { get; set; }
    DateTimeOffset Updated { get; set; }
}

Apply that interface and its properties to the entities you are working with.

public class User : IDatedEntity
{
    // ...

    public DateTimeOffset Created { get; set; }
    public DateTimeOffset Updated { get; set; }
}

Now in your database context, you can attach to the SavingChanges event and apply the desired behavior:

public class MyContext : DbContext
{
    public DbSet<User> Users { get; set; }

    public MyContext()
    {
        var objectContext = ((IObjectContextAdapter)this).ObjectContext;
        objectContext.SavingChanges += (sender, args) =>
        {
            var now = DateTimeOffset.Now;
            foreach (var entry in this.ChangeTracker.Entries<IDatedEntity>())
            {
                var entity = entry.Entity;
                switch (entry.State)
                {
                    case EntityState.Added:
                        entity.Created = now;
                        entity.Updated = now;
                        break;
                    case EntityState.Modified:
                        entity.Updated = now;
                        break;
                }
            }
            this.ChangeTracker.DetectChanges();
        };
    }
}

Note the final call to DetectChanges, which will ensure that the new updated date values are applied to all modified entities.

Sign up to request clarification or add additional context in comments.

Comments

2

If you're using .NET Core and Entity Framework Core the easiest way I found to this is to use the built-in ChangeTracker on the database context and subscribe to its event ChangeTracker_StateChanged which will trigger any time an Entity changes state. I implemented it like this:

public abstract class BaseDbContext<T> : DbContext where T : DbContext
    {
        public BaseDbContext(DbContextOptions<T> options) : base(options)
        {
            this.ChangeTracker.StateChanged += ChangeTracker_StateChanged;
        }

        private void ChangeTracker_StateChanged(object sender, EntityStateChangedEventArgs e)
        {
            if (e.NewState == EntityState.Added || e.NewState == EntityState.Modified)
            {
                var entity = (BaseEntity)e.Entry.Entity;
                entity.UpdatedDate = DateTime.UtcNow;
            }
        }
    }

I then use this BaseDbContext across several projects all of whose entities extend a BaseEntity with an UpdatedDate field.

Comments

0

You can do this only with migrations. Set defaultSqlValue parameter for UpdateDate property to "GETDATE()" or "GETUTCDATE()" and when you modify or add new entity you would need to set its value to null.

1 Comment

this results into somehow an extra select query post updation, in lots of modification scenarios, this affects the performance, as it updates and selects the updated values. Can it be done without using SQL methods ?
0

In 2022, we can use annotations to set the computed and identity columns:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime created { get; set; }

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime updated { get; set; }

https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations#default-values

To set the default value:

// Generate default value for created and updated column types, if they exist
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    modelBuilder.Entity(entityType.Name, x =>
    {
        if (entityType.FindProperty("created") != null)
            x.Property("created").HasDefaultValueSql("getdate()");

        if (entityType.FindProperty("updated") != null)
            x.Property("updated").HasDefaultValueSql("getdate()");
    });
}

2 Comments

Just as in your other answer these attributes alone don't do anything, let alone set a server DateTime value. BTW, it's nothing to do with 2022. EF uses these attributes from the first day they went code first.
Setting database defaults is a good first step (provided that they use migrations),. Note that it doesn't update UpdateDate on each modification yet. That requires a trigger.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.