pro*_*ock 2 c# entity-framework entity-framework-core
背景:每当添加或更新"Item"实体时,我都会覆盖SaveChanges()方法以自动生成LastUpdatedDate.
Item.cs
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? LastUpdated { get; set; }
Run Code Online (Sandbox Code Playgroud)
的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();
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是每当调用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;
Run Code Online (Sandbox Code Playgroud)
题:
这是在EF Core中设置计算属性的正确方法吗?
另外,我可以首先防止LastUpdated被定义为"只读"吗?
这是在EF Core中设置计算属性的正确方法吗?
该文件是在这个相当明确:
添加或更新时生成的值意味着每次保存记录(插入或更新)时都会生成新值.
警告
如何为添加和更新的实体生成值取决于所使用的数据库提供程序.(...)如果指定在添加或更新时生成DateTime属性,则必须设置生成值的方法(例如数据库触发器).
注释的名称 - "DatabaseGeneratedOption" - 可能已经在这些方面显示了一些内容.
因此,如果要使用此模式,则应在数据库中设置触发器,以在插入和每次更新时设置字段值.通过使用注释[DatabaseGenerated]
或流畅的API .ValueGeneratedOnAddOrUpdate()
(无需同时执行这两项操作),EF会发出信号,表示应在保存更改后从数据库中读取值.
至于IsReadOnlyBeforeSave
财产.到目前为止,唯一的文档是EF源代码中属性的XML文档:
获取或设置一个值,该值指示在将实体保存到数据库之前是否可以修改此属性.如果为true,则在此属性处于
Added
状态时为其分配值时将引发异常.
据我所知,您可能希望将此值设置true
为消除设置此属性有用的任何期望(因为它不是).同样,您可以设置IsReadOnlyAfterSave
在现有(非Added
)实体中设置属性时抛出异常.
如果您不想要数据库生成的值,则可以删除注释并像现在一样分配值.
小智 5
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;
}
Run Code Online (Sandbox Code Playgroud)