Closed Tishka closed 1 year ago
@Tishka you probably want to read our docs on generated values, and especially the section on date/time value generation.
tl;dr to set the date/time every time the row is updated, you will need to set up a database trigger (just configuring ValueGeneratedOnAddOrUpdate doesn't set up this mechanism in the database).
In your code above, when an entity is loaded from the database, ChangedAt is loaded along with it. When the application then makes a change, ChangeAt isn't recalculated at any point, and so EF doesn't detect any changes and so doesn't save the value to the database (even if it did, it again would be the old value since no recalculation took place).
@Tishka you probably want to read our docs on generated values, and especially the section on date/time value generation.
I was reading this section, but can't use and create this. Tried use HasDefaultValueSql("GETDATE()"), but this works on first insert, but not update
tl;dr to set the date/time every time the row is updated, you will need to set up a database trigger (just configuring ValueGeneratedOnAddOrUpdate doesn't set up this mechanism in the database).
we don't use logic in db...
In your code above, when an entity is loaded from the database, ChangedAt is loaded along with it. When the application then makes a change, ChangeAt isn't recalculated at any point, and so EF doesn't detect any changes and so doesn't save the value to the database (even if it did, it again would be the old value since no recalculation took place).
If i don't modify ChangeAt this is understand. But then i create new entity, this field is calculated, but not saved
Tried use HasDefaultValueSql("GETDATE()"), but this works on first insert, but not update
This is the expected behavior. Databases don't support computed columns with GETDATE()
.
we don't use logic in db...
Note that HasDefaultValueSql is another form of DB logic, so if HasDefaultValueSql is acceptable, a trigger isn't very different.
If i don't modify ChangeAt this is understand. But then i create new entity, this field is calculated, but not saved
This is probably because you have ValueGeneratedOnAddOrUpdate on the property, so EF assumes the value will be generated (e.g. in the database) and so doesn't send the property's actual value. If you remove ValueGeneratedOnAddOrUpdate, then you should see the value getting sent, but once again, this will only work for new entities. Entities which you load from the database won't have their property recalculated.
If you really wish to avoid database triggers, then it's up to you to trigger recalculation of the property; you could either do it manually in code, or set up some mechanism where changing any other property would also cause ChangeAt to be recalculated.
This is probably because you have ValueGeneratedOnAddOrUpdate on the property, so EF assumes the value will be generated (e.g. in the database) and so doesn't send the property's actual value. If you remove ValueGeneratedOnAddOrUpdate, then you should see the value getting sent, but once again, this will only work for new entities. Entities which you load from the database won't have their property recalculated.
I comment this code block, but don't work for data insert.
If you really wish to avoid database triggers, then it's up to you to trigger recalculation of the property; you could either do it manually in code, or set up some mechanism where changing any other property would also cause ChangeAt to be recalculated.
Yes, i think about it
Note that HasDefaultValueSql is another form of DB logic, so if HasDefaultValueSql is acceptable, a trigger isn't very different.
I can create trigger from code, not in db?
I can create trigger from code, not in db?
You can create the trigger by using raw SQL in a migration.
@Tishka Note that if you are okay with the timestamp being generated on the client, then overriding SaveChanges
and setting ChangeAt
there is easy to implement.
@Tishka Note that if you are okay with the timestamp being generated on the client, then overriding
SaveChanges
and settingChangeAt
there is easy to implement.
Hello.
Can you get examples?
I can create trigger from code, not in db?
You can create the trigger by using raw SQL in a migration.
Why don't add [DatabaseGenerated(DatabaseGeneratedOption.Computed)] for DateTime columns?
@Tishka Here's an example:
public abstract class EstimateBase
{
private DateTime? _changedAt;
[Required]
[Description("Идентификатор (ID) объекта в ИСУП КС")]
public virtual int OBJ_ID { get; set; }
public DateTime ChangedAt { get; set; }
[Required]
[Description("Версия")]
public int VERSN { get; set; }
}
public abstract class EstimateSpecificationBase : EstimateBase
{
[Description("Наименование объекта, Наименование элемента структуры")]
[MaxLength(255)]
public string FullName { get; set; }
[MaxLength(255)]
public string SHIFR { get; set; }
}
public class EstimateHeader : EstimateSpecificationBase
{
}
public class SomeDbContext : DbContext
{
private static ILoggerFactory ContextLoggerFactory
=> LoggerFactory.Create(b => b.AddConsole().SetMinimumLevel(LogLevel.Information));
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(Your.ConnectionString)
.UseLoggerFactory(ContextLoggerFactory)
.EnableSensitiveDataLogging();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EstimateBase>().HasKey(e => e.OBJ_ID);
modelBuilder.Entity<EstimateSpecificationBase>();
modelBuilder.Entity<EstimateHeader>();
}
public override int SaveChanges()
{
foreach (var entry in ChangeTracker
.Entries<EstimateBase>()
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
{
entry.Entity.ChangedAt = DateTime.UtcNow;
}
return base.SaveChanges();
}
}
public class Program
{
public static void Main()
{
using (var context = new SomeDbContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.Add(new EstimateHeader());
context.SaveChanges();
}
using (var context = new SomeDbContext())
{
var header = context.Set<EstimateHeader>().Single();
header.FullName = "A";
context.SaveChanges();
}
}
}
@Tishka Here's an example:
public abstract class EstimateBase { private DateTime? _changedAt; [Required] [Description("Идентификатор (ID) объекта в ИСУП КС")] public virtual int OBJ_ID { get; set; } public DateTime ChangedAt { get; set; } [Required] [Description("Версия")] public int VERSN { get; set; } } public abstract class EstimateSpecificationBase : EstimateBase { [Description("Наименование объекта, Наименование элемента структуры")] [MaxLength(255)] public string FullName { get; set; } [MaxLength(255)] public string SHIFR { get; set; } } public class EstimateHeader : EstimateSpecificationBase { } public class SomeDbContext : DbContext { private static ILoggerFactory ContextLoggerFactory => LoggerFactory.Create(b => b.AddConsole().SetMinimumLevel(LogLevel.Information)); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder .UseSqlServer(Your.ConnectionString) .UseLoggerFactory(ContextLoggerFactory) .EnableSensitiveDataLogging(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<EstimateBase>().HasKey(e => e.OBJ_ID); modelBuilder.Entity<EstimateSpecificationBase>(); modelBuilder.Entity<EstimateHeader>(); } public override int SaveChanges() { foreach (var entry in ChangeTracker .Entries<EstimateBase>() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)) { entry.Entity.ChangedAt = DateTime.UtcNow; } return base.SaveChanges(); } } public class Program { public static void Main() { using (var context = new SomeDbContext()) { context.Database.EnsureDeleted(); context.Database.EnsureCreated(); context.Add(new EstimateHeader()); context.SaveChanges(); } using (var context = new SomeDbContext()) { var header = context.Set<EstimateHeader>().Single(); header.FullName = "A"; context.SaveChanges(); } } }
Thank you very match! I will try use
Hello.
I have next c# class models:
I want the value of the "public DateTime ChangedAt" column to change automatically when a record is inserted / changed in the table. I am add [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribyte or add next code in method protected override void OnModelCreating(ModelBuilder modelBuilder) of dbContext :
But, how i read this is not work for DateTime column and depend on db provider behavior. Maybe you can say me, how i can do this?
Then i added next code for class property:
Db migration for this table is:
Then i insert entity in table, in column ChangedAt i have value: '0001-01-01 00:00:00.0000000' Insert in db as:
Use SQL Server Profiler i get next request:
Why i get some result? Why field has such value?
If i update this record field ChangedAt updated to class value. Update as:
P.S. If i use method HasDefaultValueSql() as:
on insert it work fine, but on update column don't changed value from class field.