【问题标题】:EF core computed properties marked as readonly标记为只读的 EF 核心计算属性
【发布时间】:2016-09-01 18:20:36
【问题描述】:

背景:我正在重写 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() 时,我都会收到此异常:

EntityFramework.Core.dll 中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理。
附加信息:实体类型“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 被定义为“只读”吗?

【问题讨论】:

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


    【解决方案1】:

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

    documentation 对此非常清楚:

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

    注意

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

    注释的名称——“DatabaseGeneratedOption”——可能揭示了这些方面的内容。

    所以如果你想使用这种模式,你应该在数据库中设置一个触发器,在插入和每次更新时设置字段值。通过使用注解[DatabaseGenerated]流畅的API.ValueGeneratedOnAddOrUpdate()(不需要两者都做),EF 被告知它应该在保存更改后从数据库中读取值。

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

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

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

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

    【讨论】:

    • 是的,所以不需要注释,因为我没有在数据库上生成,我现在明白了。但是 Metadata.IsReadOnlyBeforeSave 设置呢?是否建议将每个计算属性都设置为 false?
    【解决方案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;
    }
    

    【讨论】:

      【解决方案3】:

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

      投掷

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

      保存

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

      忽略

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

      【讨论】:

        猜你喜欢
        • 2021-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-30
        相关资源
        最近更新 更多