如何自动填充CreatedDate和ModifiedDate?

时间:2016-07-04 10:57:10

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

我正在学习ASP.NET Core MVC,我的模型是

namespace Joukyuu.Models
{
    public class Passage
    {
        public int PassageId { get; set; }
        public string Contents { get; set; }


        public DateTime CreatedDate { get; set; }
        public DateTime ModifiedDate { get; set; }
    }
}

Passage表用于保存我写的段落。

方案

  • Create视图只有一个字段Contents来输入一个段落。服务器必须自动将CreatedDateModifiedDate设置为相等(使用UTC格式)。

  • Edit视图只有一个字段Contents来编辑一个段落。 ModifiedDate必须由服务器自动设置。

问题

我需要将哪些属性附加到CreatedDateModifiedDate属性,以便服务器根据上述方案自动填充这些属性?

6 个答案:

答案 0 :(得分:19)

  

我必须将哪些属性附加到CreatedDate和ModifiedDate属性,以使服务器根据上述方案自动填充这些属性?

解决方案1)

namespace Joukyuu.Models
{
    public class Passage
    {
        public int PassageId { get; set; }
        public string Contents { get; set; }


        public DateTime CreatedDate { get; set; }
        public DateTime ModifiedDate { get; set; }

       public Passage()
       {          
         this.CreatedDate  = DateTime.UtcNow;
         this.ModifiedDate = DateTime.UtcNow;
       }
    }
}

通过编辑,你必须自己更改/更新它!

解决方案2)

自定义属性:

[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDate { get; set; }

Entity Framework 6 Code first Default value

解决方案3)

在Computed:

的帮助下
[Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedUtc { get; set; 


  "dbo.Products",
            c => new
                {
                    ProductId = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                    CreatedUtc = c.DateTime(nullable: false, defaultValueSql: "GETUTCDATE()"),
                })
            .PrimaryKey(t => t.ProductId);

https://andy.mehalick.com/2014/02/06/ef6-adding-a-created-datetime-column-automatically-with-code-first-migrations/

解决方案4) 您也可以通过手动修改查询来执行命令拦截器。

解决方案5) 使用Repository模式管理数据创建并通过CreateNew进行设置 这是我最喜欢的解决方案!

https://msdn.microsoft.com/en-us/library/ff649690.aspx

解决方案6) 只需在UI或虚拟机中设置或进入。

Entity Framework Core 1.0 easy:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Passage>()
        .Property(b => b.CreatedDate )
        .HasDefaultValueSql("getdate()");
}

答案 1 :(得分:5)

对于那些使用异步系统(SaveChangesAsync)和.net核心的人来说,最好将下面的代码添加到DbContext类。

    public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
    {
        var AddedEntities = ChangeTracker.Entries().Where(E => E.State == EntityState.Added).ToList();

        AddedEntities.ForEach(E =>
        {
            E.Property("CreationTime").CurrentValue = DateTime.Now;
        });

        var EditedEntities = ChangeTracker.Entries().Where(E => E.State == EntityState.Modified).ToList();

        EditedEntities.ForEach(E =>
        {
            E.Property("ModifiedDate").CurrentValue = DateTime.Now;
        });

        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

您也可以定义类或接口,如下所示。

public class SaveConfig
{
    public DateTime CreationTime { get; set; }
    public DateTime? ModifiedDate { get; set; }
}

只是从SaveConfig类继承实体。

答案 2 :(得分:4)

如果您使用的是代码,可以试试这个

[Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate { get; set; }

迁移

AddColumn("Passage", "CreatedDate", n => n.DateTime(nullable: false, defaultValueSql: "GETDATE()"));

更多参考heresimilar answer

  

或者您可以全局覆盖saveChanges注意*如果您拥有CreatedDate字段,则会对整个模型产生影响

public override int SaveChanges()
{
  DateTime saveTime = DateTime.Now;
  foreach (var entry in this.ChangeTracker.Entries()
      .Where(e => e.State == (EntityState) System.Data.EntityState.Added))
   {
     if (entry.Property("CreatedDate").CurrentValue == null)
       entry.Property("CreatedDate").CurrentValue = saveTime;
   }
   return base.SaveChanges();  
}

答案 3 :(得分:1)

使用 SaveChangesAsync 的另一种方法是,您可以为相应的数据库创建数据库触发器。我制作了一个gist,其中包含用于 mssql,postgress,sqlite和mysql 的一些辅助功能。这是代码的粘贴。

using Microsoft.EntityFrameworkCore.Metadata;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;

namespace ProjectNamespace.Api.Utils
{
    public class DatabaseTools
    {
        public class DatabaseKeys
        {
            public string ValueGenerationStrategy;
            public object SerialColumn;
            public string DEFAULTDATE_CREATE;
            public string DEFAULTDATE_UPDATE;
            public string dateTime;
            public bool UpdateDateTrigger = true;
            public string ProviderName { get; set; }
        }
        /*
        Notes:
        can check if updateDateTrigger needed for dataprovider and add update triggers
        if (dbKeys.UpdateDateTrigger)
            {
                migrationBuilder.Sql(DatabaseTools.getUpdateDateTrigger(dbKeys.ProviderName, "TG_roles_updated_at", "roles", "updated_at"));
            }
        if postgress don't forget to drop the generated functions for each of the different named update columns (updated_at, modified_date) at the end of the migration E.g.
        protected override void Down(MigrationBuilder migrationBuilder){...
        if (dbKeys.ProviderName == "postgress")
            {
                migrationBuilder.Sql(@"
                    DROP FUNCTION update_" + "updated_at" + @"_column();
                    ");
            }
        postgres doesn't support parameters in triggers for new and old keywords.
         */
        /// <summary>
        /// needed for non mysql implementation to update a datetime on a record update
        /// </summary>
        /// <param name="databaseProvider"></param>
        /// <param name="name"></param>
        /// <param name="table"></param>
        /// <param name="column"></param>
        /// <param name="schema"></param>
        /// <param name="id_column"></param>
        /// <returns></returns>
        public static string getUpdateDateTrigger(string databaseProvider, string name, string table, string column, string schema = "", string id_column = "id")
        {
            string updateDateTrigger = null;
            if (!string.IsNullOrEmpty(schema))
                schema = schema + ".";
            switch (databaseProvider)
            {
                case "sqlite":
                    updateDateTrigger = @"
                    CREATE TRIGGER [" + name + @"]  
                        AFTER   
                        UPDATE  
                        ON " + table + @"
                        FOR EACH ROW   
                        WHEN NEW." + column + @" <= OLD." + column + @"  
                    BEGIN  
                        update " + table + @" set " + column + @"=CURRENT_TIMESTAMP where " + id_column + @"=OLD." + id_column + @";  
                    END  
                    ";
                    break;
                case "postgress":
                    updateDateTrigger = @"
                    CREATE OR REPLACE FUNCTION update_" + column + @"_column() RETURNS TRIGGER AS
                    $$
                    BEGIN
                        NEW." + column + @" = now();
                        RETURN NEW;  
                    END;
                    $$ LANGUAGE plpgsql;
                    CREATE TRIGGER " + name + @" 
                    BEFORE UPDATE ON " + schema + @"""" + table + @"""
                    FOR EACH ROW EXECUTE PROCEDURE update_" + column + @"_column('" + column + @"');
                    ";
                    break;
                case "sqlserver":
                    updateDateTrigger = @"
                    CREATE TRIGGER " + name + @" 
                    ON " + table + @"
                    AFTER UPDATE AS
                        UPDATE " + table + @"
                        SET " + column + @" = GETDATE()
                        WHERE " + id_column + @" IN (SELECT DISTINCT " + id_column + @" FROM Inserted);
                    ";
                    break;
            }

            return updateDateTrigger;
        }
        /*
        Notes:
        if in OnModelCreating can call as var dbKeys = DatabaseTools.getDatabaseDefaults(this.Database.ProviderName);
        if in a migration call as var dbKeys = DatabaseTools.getDatabaseDefaults(migrationBuilder.ActiveProvider);
        Then update any datetime columns for correct defaults
        created_at = table.Column<DateTime>(type: dbKeys.dateTime, nullable: true, defaultValueSql: dbKeys.DEFAULTDATE_CREATE),
        updated_at = table.Column<DateTime>(type: dbKeys.dateTime, nullable: true, defaultValueSql: dbKeys.DEFAULTDATE_UPDATE)
         */
        /// <summary>
        /// Can get specific formats for different databases. Supports mssql, postgress, sqlite, and mysql.
        /// </summary>
        /// <param name="databaseProvider"></param>
        /// <returns></returns>
        public static DatabaseKeys getDatabaseDefaults(string databaseProvider)
        {
            var dbKeys = new DatabaseKeys();
            dbKeys.dateTime = "datetime";

            switch (databaseProvider)
            {
                case "Microsoft.EntityFrameworkCore.Sqlite":
                case "sqlite":
                    dbKeys.DEFAULTDATE_UPDATE = "datetime('now')";
                    dbKeys.SerialColumn = true;
                    dbKeys.ProviderName = "sqlite";
                    dbKeys.ValueGenerationStrategy = "Sqlite:Autoincrement";
                    break;
                case "postgress":
                case "Npgsql.EntityFrameworkCore.PostgreSQL":
                    dbKeys.DEFAULTDATE_UPDATE = "CURRENT_TIMESTAMP";
                    dbKeys.SerialColumn = NpgsqlValueGenerationStrategy.SerialColumn;
                    dbKeys.ProviderName = "postgress";
                    dbKeys.ValueGenerationStrategy = "Npgsql:ValueGenerationStrategy";
                    dbKeys.dateTime = "timestamp";
                    break;
                case "mysql":
                case "MySql.Data.EntityFrameworkCore":
                    dbKeys.DEFAULTDATE_UPDATE = "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP";
                    dbKeys.DEFAULTDATE_CREATE = "CURRENT_TIMESTAMP";
                    dbKeys.UpdateDateTrigger = false;
                    dbKeys.SerialColumn = true;
                    dbKeys.ProviderName = "mysql";
                    dbKeys.ValueGenerationStrategy = "MySQL:AutoIncrement";
                    break;
                case "sqlserver":
                default:
                    dbKeys.DEFAULTDATE_UPDATE = "(getdate())";
                    dbKeys.SerialColumn = SqlServerValueGenerationStrategy.IdentityColumn;
                    dbKeys.ProviderName = "sqlserver";
                    dbKeys.ValueGenerationStrategy = "SqlServer:ValueGenerationStrategy";
                    break;
            }
            if (string.IsNullOrEmpty(dbKeys.DEFAULTDATE_CREATE))
                dbKeys.DEFAULTDATE_CREATE = dbKeys.DEFAULTDATE_UPDATE;
            return dbKeys;
        }
    }
}

答案 4 :(得分:1)

PostgreSQL中CreationDate的解决方案:

builder.Property(e => e.CreationDate)
  .HasColumnType("timestamp without time zone")
  .HasDefaultValueSql("NOW()")
  .ValueGeneratedOnAdd();

不幸的是,根据文档,http://www.npgsql.org/efcore/value-generation.html#computed-columns-on-add-or-update

尚无针对更新事件的解决方案

答案 5 :(得分:-2)

太简单了,只需执行2步:

1。创建模型,其中这些字段位于createDate字段的顶部:

 [Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)]

2。创建迁移后,在其文件中更新此部分的数据库编辑列,并像这样添加defaultValueSql: "getdate()"

 id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                CreatedDate = table.Column<DateTime>(defaultValueSql: "getdate()",nullable: false)
   
相关问题