【问题标题】:Automatic CreatedAt and UpdatedAt fields OnModelCreating() in ef6ef6 中的自动 CreatedAt 和 UpdatedAt 字段 OnModelCreating()
【发布时间】:2018-01-07 20:12:55
【问题描述】:

我的User 模型中有CreatedAtUpdatedAt 列。

User.cs

public string Name { get; set; }
public DateTime? CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }

要求

  • 当我们SaveChanges()用户记录时,CreatedAtUpdatedAt应该自动保存例如:DateTime.UtcNow
  • 当我更新User 记录时,只有UpdatedAt 列应该更新为当前日期时间。
  • 对于所有其他型号,这应该会自动发生,可能是OnModelCreating() 中的某些配置。
  • 我希望这种行为能够从数据库和其他地方找到latest 记录。
  • 我正在使用code first迁移方法
  • 我正在使用 MySQL 服务器,MySql.DataMySql.Data.Entity.EF6

更新

我添加了BaseEntity.cs模型

public abstract class BaseEntity
    {
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }
    }

从 BaseEntity 继承用户

public class User : BaseEntity
{
  public int Id { get; set; }
  public int FullName { get; set; }
}

并更新了迁移以包括 defaultValueSql()

AddColumn("dbo.Users", "CreatedAt", c => c.DateTime(nullable: false, precision: 0, defaultValueSql: "NOW()"));
AddColumn("dbo.Users", "UpdatedAt", c => c.DateTime(nullable: false, precision: 0, defaultValueSql: "NOW()"));"

现在,需要一种方法来修复每次更新中的 UpdatedAt 列。

【问题讨论】:

  • 先用数据库,再用sql实现?
  • 不,我先使用代码。我找到了AddColumn("dbo.Users", "CreatedAt", c => c.DateTime(nullable: false, precision: 0, defaultValueSql: "UTC_TIMESTAMP()"));update-migration 给出了语法错误。
  • 但在 mysql 查询中运行 select UTC_TIMESTAMP(); 会给出当前日期时间戳
  • You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'UTC_TIMESTAMP()' at line 1

标签: c# asp.net-mvc-5 entity-framework-6


【解决方案1】:

终于找到了解决我的问题的方法。因为我们可以将数据库从MySql 更改为postgresqlMs Sql server,所以使用sql 查询添加默认值似乎不是正确的解决方案。

这是我的解决方法。

添加Base型号

 public abstract class BaseEntity
 {
    public DateTime? CreatedAt { get; set; }
    public DateTime? UpdatedAt { get; set; }
 }

从这个基础模型继承你的所有模型,在我的例子中是User

public class User : BaseEntity
{
  public int Id { get; set; }
  public int FullName { get; set; }
}

如果您使用代码优先方法,请不要忘记生成迁移。迁移应该足够简单:

例子:

AddColumn("dbo.Users", "CreatedAt", c => c.DateTime(precision: 0));
AddColumn("dbo.Users", "UpdatedAt", c => c.DateTime(precision: 0));

最后一步是在您的上下文中覆盖 SaveChanges()SaveChangesAsync()

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

    public override int SaveChanges()
    {
        AddTimestamps();
        return base.SaveChanges();
    }

    public override async Task<int> SaveChangesAsync()
    {
        AddTimestamps();
        return await base.SaveChangesAsync();
    }

    private void AddTimestamps()
    {
        var entities = ChangeTracker.Entries()
            .Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));

        foreach (var entity in entities)
        {
            var now = DateTime.UtcNow; // current datetime

            if (entity.State == EntityState.Added)
            {
                ((BaseEntity)entity.Entity).CreatedAt = now;
            }
            ((BaseEntity)entity.Entity).UpdatedAt = now;
        }
    }
}

【讨论】:

  • 请注意,对于那些使用 EF Core 的人,这不起作用。实体还不能从抽象基类继承字段。 MS 将此称为 Table-Per-Concrete-Type,但尚不支持。功能发布目标是 EF 5.0,目前处于预览阶段,希望很快。这将使许多领域模型更加清晰。 docs.microsoft.com/en-us/ef/core/modeling/inheritance
【解决方案2】:

您可以像这样定义自己的interface

public interface ITiming {
  DateTime CreatedAt {get; set; }
  DateTime UpdatedAt {get; set; }
}

现在让你想要的每个模型都实现这个接口。 并像这样给自己写一个ExtionsionMethod

public static SaveChangesTimed(this YourDbContext _context, ITiming timed) {
   timed.CreatedAt = DateTime.Now;
   timed.UpdatedAt = Datetime.Now;
   _context.SaveChanges();
}

【讨论】:

  • 谢谢 Yggraz,但我有一个问题:看起来它会更新 CreatedAtUpdatedAt 每次 SaveChanges() 被击中,但我想第一次更新 CreatedAt从那时起它不应该改变,只有UpdatedAt 应该从下一次改变。如果我误解了,请纠正我。
  • 是的,如果你想要这个,那么你必须自动实现 CreatedAt link 并使用仅用于 UpdatedAt 的扩展
【解决方案3】:

我看到@przbadu 的帖子,我有点困难。 我正在使用 dotnet core 2.2,我需要将 SaveChangeAsync 方法更改为:

public override int SaveChanges()
{
    AddTimestamps();
    return base.SaveChanges();
}

public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
    AddTimestamps();
    return base.SaveChangesAsync();
}

private void AddTimestamps()
{
    var entities = ChangeTracker.Entries()
        .Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));

    foreach (var entity in entities)
    {
        var now = DateTime.UtcNow; // current datetime

        if (entity.State == EntityState.Added)
        {
            ((BaseEntity)entity.Entity).CreatedAt = now;
        }
        ((BaseEntity)entity.Entity).UpdatedAt = now;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多