Reputation: 430
Coming from working with PHP, it was rather straight forward working with Timestamps but with Entity Framework, it's sort of a headache.
Upvotes: 1
Views: 9314
Reputation: 388
I'm pretty sure you are using timestamps to handle some concurrency conflict. There is actually two ways in doing so in Entity Framework (Core)
You can configure concurrency token using Data Annotations on your Entity class.
public class BookEntity
{
public int BookId {get; set;}
public string Title {get; set;}
// This tells EF that this property is a concurrency token,
// which means EF will check it hasn't changed when you update it
[ConcurrencyCheck]
public DateTime PublishedOn {get; set;}
// Other properties follow...
}
Concurrency check can also be configured using the fluent API
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<BookEntity>()
.Property(p=> p.PublishedOn)
.IsConcurrencyToken();
// ...other configurations follow...
}
You can configure timestanps using Data Annotations on your Entity class.
public class BookEntity
{
public int BookId {get; set;}
public string Title {get; set;}
// This tells EF to mark ChangeCheck property as a timestamp,
// This causes EF to check this when updating to see if this has changed
[Timestamp]
public byte[] ChangeCheck {get; set;}
// Other properties follow...
}
Configuring Timestamp using the Fluent API
protected override void OnModelCreating(ModelBuilder builder)
{
// Value of ChangeCheck will be changed each time the row
// created/updated
builder.Entity<BookEntity>()
.Property(p=> p.ChangeCheck)
.IsRowVersion;
// ...other configurations follow...
}
Both configurations create a column in a table that the database server will automatically change whenever there's an INSERT or UPDATE to that table.
This can be done only via the Fluent API
public class BookEntity
{
public int BookId {get; set;}
public string Title {get; set;}
// Column you set up as computed
// You give it a private setter, as its a read-only property
public DateTime DateModified {get; private set;}
// Other properties follow...
}
Then you configure the column via the Fluent API.
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<BookEntity>()
.Property(p=> p.DateModified)
.HasComputedColumnSql("getutcdate()")
.ValueGeneratedOnAddOrUpdate();
// ...other configurations follow...
}
.HasComputedColumnSql("getutcdate()")
will tell EF to compute a value for the column; in this case to get the current utc datetime, then .ValueGeneratedOnAddOrUpdate()
, will let EF know that the column is computed thus should update the column on any update made.
Upvotes: 3
Reputation: 430
Here's a solution that worked for me. First I had a Baseentity that all entities inherited from:
public abstract class DbEntity
{
[Column("created_at")]
public DateTime CreatedAt { get; set; } = DateTime.Now;
[Column("updated_at")]
public DateTime UpdatedAt { get; set; } = DateTime.Now;
}
Then I added an extension method for the UpdateMethod:
public static class DbSetExtension
{
public static EntityEntry<TEntity> UpdateCustom<TEntity>(this DbSet<TEntity> dbSet, TEntity dbEntity)
where TEntity : class
{
dbEntity.GetType().GetProperty("UpdatedAt")?.SetValue (dbEntity, DateTime.Now, null);
return dbSet.Update(dbEntity);
}
}
This works fine for me. I'll appreciate feedback on how others handle this scenario.
P.S: The [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
approach didn't work for me even after setting default SQL for the columns.
Upvotes: 2