【问题标题】:Entity Framework Code First DateTime field update on modificationEntity Framework Code First DateTime 字段在修改时更新
【发布时间】:2022-02-24 21:57:13
【问题描述】:

我有以下实体:

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; }
}

如何使User 实体的UpdateDate 字段在更新时成为服务器DateTime?也就是说,每当数据库中的实体被修改时,UpdateDate 字段就会更改为确切的日期和时间,就像说UpdateDate = new DateTime(Datetime.Now.Ticks)

【问题讨论】:

  • @Patrick:嗨,我想在更新记录后使用 context.SaveChanges() 时将服务器日期分配给 UpdateDate 字段。
  • 正确执行此操作的唯一方法是通过数据库触发器。无法判断在获取数据库日期/时间和实体的实际提交之间会有多少延迟。

标签: c# entity-framework datetime ef-code-first entity-framework-migrations


【解决方案1】:

首先,如果您要存储当地时间,请考虑DateTimeOffset 是更好的选择。或者,您可以使用基于 UTC 的 DateTime,但我将在此示例中显示 DateTimeOffset。切勿为此目的存储基于本地的DateTime,因为您将遇到时区和夏令时问题。另见:The Case Against DateTime.Now

接下来,考虑一个可用于您希望跟踪创建/更新值的实体的通用接口。

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

将该接口及其属性应用于您正在使用的实体。

public class User : IDatedEntity
{
    // ...

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

现在在您的数据库上下文中,您可以附加到 SavingChanges 事件并应用所需的行为:

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();
        };
    }
}

注意对DetectChanges 的最终调用,这将确保将新的更新日期值应用于所有修改的实体。

【讨论】:

    【解决方案2】:

    您只能通过迁移来做到这一点。将 UpdateDate 属性的 defaultSqlValue 参数设置为“GETDATE()”或“GETUTCDATE()”,当您修改或添加新实体时,您需要将其值设置为 null。

    【讨论】:

    • 这会以某种方式导致额外的选择查询更新后,在许多修改场景中,这会影响性能,因为它会更新并选择更新的值。不使用SQL方法可以吗?
    【解决方案3】:

    如果您使用 .NET Core 和 Entity Framework Core,我发现最简单的方法是在数据库上下文中使用内置 ChangeTracker 并订阅其事件 ChangeTracker_StateChanged,该事件将在任何时候触发实体改变状态。 我是这样实现的:

    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;
                }
            }
        }
    

    然后,我在多个项目中使用此 BaseDbContext,所有这些项目的实体都使用 UpdatedDate 字段扩展 BaseEntity

    【讨论】:

      【解决方案4】:

      2022年,我们可以使用注解了:

      [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
      public DateTime CreateDate { get; set; }
      
      [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
      public DateTime UpdateDate { get; set; }
      

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

      【讨论】:

        猜你喜欢
        • 2011-10-24
        • 1970-01-01
        • 1970-01-01
        • 2015-04-12
        • 2011-10-29
        • 1970-01-01
        • 2011-12-24
        • 1970-01-01
        • 2011-08-05
        相关资源
        最近更新 更多