EF核心计算属性标记为只读

时间:2016-09-01 18:20:36

标签: c# entity-framework entity-framework-core

背景:每当添加或更新“Item”实体时,我都会覆盖SaveChanges()方法以自动生成LastUpdatedDate。

Item.cs

  [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
  public DateTime? LastUpdated { get; set; }

的DbContext

  protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      ...
      // generated value
      modelBuilder.Entity<Item>()
                  .Property(b => b.LastUpdated)
                  .ValueGeneratedOnAddOrUpdate();

     }

    public override int SaveChanges()
    {
        var now = DateTime.UtcNow;

        foreach (var item in ChangeTracker.Entries<Item>()
            .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
        {
            item.Property("LastUpdated").CurrentValue = now;             
        }           

        return base.SaveChanges();
    } 

我遇到的问题是每当调用SaveChanges()时我都会遇到此异常:

  

EntityFramework.Core.dll中出现'System.InvalidOperationException'类型的异常,但未在用户代码中处理。
  附加信息:实体类型'Item'上的属性'LastUpdated'在保存后被定义为只读,但其值已被修改或标记为已修改。

要解决此问题,我必须设置IsReadOnlyBeforeSave和IsReadOnlyAfterSave 为假,如下所示:

        modelBuilder.Entity<Tray>()
            .Property(b => b.LastUpdated)
            .ValueGeneratedOnAddOrUpdate()
            .Metadata.IsReadOnlyBeforeSave = false; 
        modelBuilder.Entity<Tray>()
            .Property(b => b.LastUpdated)
            .Metadata.IsReadOnlyAfterSave = false;

问题:

这是在EF Core中设置计算属性的正确方法吗?

此外,我是否可以首先将LastUpdated定义为“只读”?

3 个答案:

答案 0 :(得分:6)

  

这是在EF Core中设置计算属性的正确方法吗?

documentation对此很清楚:

  

在添加或更新时生成的值意味着每次保存记录(插入或更新)时都会生成新值。

     

<强>注意

     

如何为添加和更新的实体生成值取决于所使用的数据库提供程序。 (...)如果指定在添加或更新时生成DateTime属性,则必须设置生成值的方法(例如数据库触发器)。

注释的名称 - “DatabaseGeneratedOption” - 可能已经在这些方面显示了一些内容。

因此,如果要使用此模式,则应在数据库中设置触发器,以在插入和每次更新时设置字段值。通过使用注释[DatabaseGenerated] 使用流畅的API .ValueGeneratedOnAddOrUpdate()(无需同时执行这两项操作),EF会在保存更改后通知它应该从数据库中读取值。 / p>

至于IsReadOnlyBeforeSave属性。到目前为止,唯一的文档是EF源代码中属性的XML文档:

  

获取或设置一个值,该值指示在将实体保存到数据库之前是否可以修改此属性。如果为true,则当此属性处于Added状态时,将抛出异常。

据我所知,您可能希望将此值设置为true以消除设置此属性有用的任何期望(因为它不是)。同样,您可以设置IsReadOnlyAfterSave在现有(不是Added)实体中设置属性时抛出异常。

如果您不想要数据库生成的值,可以删除注释并像现在一样分配值。

答案 1 :(得分:2)

IsReadOnlyAfterSave 标记已废弃,并由 AfterSaveBehavior 取代。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Device>()
        .Property<string>("TenantId")
        .HasField("_tenantId")
        //.Metadata.IsReadOnlyAfterSave = true;
        .Metadata.AfterSaveBehavior = Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore;
}

答案 2 :(得分:0)

EF Core 3.x和5

这是在EF Core 5中设置AfterSaveBehavior的方式

    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 
       modelBuilder.Entity<MyEntity>
         .Property(x => x.DateCreated)
         .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save); 
    }

投掷

如果设置了显式值或更改了值,则将引发异常。这是默认,这就是为什么您会收到异常的原因-根据您的情况将其设置为“保存”或“忽略”。

保存

设置或更改的值将以正常方式发送到数据库。

忽略

任何设置或更改的值都将被忽略。