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



Background:我重写SaveChanges()方法,以便在添加或更新"Item"实体时自动生成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()被调用时,我得到这个异常:

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

为了解决这个问题,我不得不设置IsReadOnlyBeforeSave和IsReadOnlyAfterSave到false,如下所示:

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

问题:

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

另外,我可以防止LastUpdated首先被定义为"只读"吗?

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

文档对此非常清楚:

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

谨慎

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

注释的名称——"DatabaseGeneratedOption"——可能已经揭示了这些内容。

因此,如果您想使用这种模式,您应该在数据库中设置一个触发器,在插入和每次更新时设置字段值。通过使用注释[DatabaseGenerated] 流畅API .ValueGeneratedOnAddOrUpdate()(不需要同时进行),EF被告知应该在保存更改后从数据库中读取值。

对于IsReadOnlyBeforeSave属性。目前唯一的文档是EF源代码中关于该属性的XML文档:

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

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

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

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

EF Core 3。X和5

这是如何设置AfterSaveBehavior在EF Core 5

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

如果设置了显式值或更改了该值,则会抛出异常。这是默认的,这就是为什么你会得到一个异常-根据你的情况将其设置为Save或Ignore。

保存

设置或修改后的值将按正常方式发送到数据库。

忽略

最新更新