C# 如何设置阴影属性的默认值

C# 如何设置阴影属性的默认值,c#,entity-framework-core,C#,Entity Framework Core,我拥有以下实体: public class Person { public Guid Id { get; set; } public string Name { get; set; } } 这是我的数据库上下文 public class PersonDbContext : DbContext { private static readonly ILoggerFactory Logger = LoggerFactory.Create(x => x.

我拥有以下实体:

public class Person
{
    public Guid Id { get; set; }

    public string Name { get; set; }
}
这是我的数据库上下文

public class PersonDbContext : DbContext
{
    private static readonly ILoggerFactory
        Logger = LoggerFactory.Create(x => x.AddConsole());

    public DbSet<Person> Persons { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseLoggerFactory(Logger)
            .UseSqlServer(
                "Server=(localdb)\\mssqllocaldb;Database=PersonDb;Trusted_Connection=True;MultipleActiveResultSets=true");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder
            .Entity<Person>()
            .Property<DateTime>("Created")
            .HasDefaultValueSql("GETUTCDATE()")
            .ValueGeneratedOnAdd();

        modelBuilder
            .Entity<Person>()
            .Property<DateTime>("Updated")
            .HasDefaultValueSql("GETUTCDATE()")
            .ValueGeneratedOnAddOrUpdate();
    }
}
我可以确认在插入新人员时这两个属性都设置为UTC日期。 但是,在更新时,
Updated
属性未设置

这是生成的t-sql:

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@p1='?' (DbType = Guid), @p0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      UPDATE [Persons] SET [Name] = @p0
      WHERE [Id] = @p1;
      SELECT [Updated]
      FROM [Persons]
      WHERE @@ROWCOUNT = 1 AND [Id] = @p1;
阅读时,我看到以下警告:

但是,如果指定在添加时生成DateTime属性 或更新,则必须设置生成值的方法。 一种方法是配置GETDATE()的默认值(请参阅 默认值)为新行生成值。然后你可以使用 在更新期间生成值的数据库触发器(例如 下面的示例(触发器)

我不明白
ValueGeneratedOnAddOrUpdate()
的目的是什么,如果它的行为类似于
ValueGeneratedOnAdd()
,我必须手动干预(创建触发器)来设置此属性

实际上,如果我将
Updated
shadow属性的定义更改为

modelBuilder
    .Entity<Person>()
    .Property<DateTime>("Updated")
    .HasDefaultValueSql("GETUTCDATE()")
    .ValueGeneratedOnAdd();
这就是我们所期望的

所以问题是-在EF Core中为阴影属性设置默认值的正确方法是什么

这是我更大项目中的简化示例,因此在
OnModelCreating
覆盖中的实体上使用
HasData
不是一个好选项(因为有许多实体)

我使用的是EF Core 3.1.1

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1"/>

如果希望具有可重用的卷影属性,请遵循以下步骤

1-创建一个标记空接口<代码>IAuditableEntity.cs

//
///它是一个标记接口,以使我们的实体可以审计。
///使用此界面标记的每个实体都会将审核信息保存到数据库中。
/// 
公共接口IAuditableEntity
{ }
2-创建一个静态类来编写阴影属性逻辑<代码>AuditableShadowProperties.cs

公共静态类AuditableShadowProperties{
公共静态只读函数EfPropertyCreatedDateTime=
实体=>EF.Property(实体,CreatedDateTime);
公共静态只读字符串CreatedDateTime=nameof(CreatedDateTime);
公共静态只读函数EfPropertyModifiedDateTime=
entity=>EF.Property(entity,ModifiedDateTime);
公共静态只读字符串ModifiedDataTime=nameof(ModifiedDataTime);
公共静态void AddAuditableShadowProperties(此ModelBuilder ModelBuilder){
modelBuilder.Model中的foreach(var entityType
.GetEntityTypes()
.Where(e=>typeof(IAuditableEntity).IsAssignableFrom(e.ClrType))){
modelBuilder.Entity(entityType.ClrType)
.属性(CreatedDateTime);
modelBuilder.Entity(entityType.ClrType)
.属性(ModifiedDateTime);
}
}
公共静态无效SetAuditableEntityPropertyValue(
此更改跟踪程序(更改跟踪程序){
var now=DateTimeOffset.UtcNow;
var modifiedEntries=changeTracker.Entries()
其中(x=>x.State==EntityState.Modified);
foreach(modifiedEntry中的变量modifiedEntry){
属性(ModifiedDateTime).CurrentValue=now;
}
var addedEntries=changeTracker.Entries()
其中(x=>x.State==EntityState.Added);
foreach(附加项中的var附加项){
属性(CreatedDateTime).CurrentValue=now;
}
}
}
3-将必要的更改添加到
PersonDbContext
中,以使用
IAAuditableEntity

//首先,我们在下一次迁移中将影子属性添加到数据库中
模型创建时受保护的覆盖无效(ModelBuilder)
{
...
builder.AddAuditableShadowProperties();
}
//重写saveChanges方法以使用阴影属性。
公共覆盖int SaveChanges()
{
ChangeTracker.DetectChanges();
BeforeSaveTriggers();
ChangeTracker.AutoDetectChangesEnabled=
false;//出于性能原因,避免再次调用DetectChanges()。
var result=base.SaveChanges();
ChangeTracker.AutoDetectChangesEnabled=true;
返回结果;
}
公共覆盖任务SaveChangesSync(CancellationToken CancellationToken=new CancellationToken())
{
ChangeTracker.DetectChanges();
BeforeSaveTriggers();
ChangeTracker.AutoDetectChangesEnabled=
false;//出于性能原因,避免再次调用DetectChanges()。
var结果=base.saveChangesSync(cancellationToken);
ChangeTracker.AutoDetectChangesEnabled=true;
返回结果;
}
公共覆盖任务savechangesync(bool acceptAllChangesOnSuccess,
CancellationToken CancellationToken=新的CancellationToken()
{
ChangeTracker.DetectChanges();
BeforeSaveTriggers();
ChangeTracker.AutoDetectChangesEnabled=
false;//出于性能原因,避免再次调用DetectChanges()。
var result=base.saveChangesSync(acceptAllChangesOnSuccess,cancellationToken);
ChangeTracker.AutoDetectChangesEnabled=true;
返回结果;
}
#区域“外部方法”
公共T GetShadowPropertyValue(对象实体,字符串propertyName),其中T:IConvertible
{
var value=this.Entry(entity).Property(propertyName).CurrentValue;
返回值!=null?
(T) Convert.ChangeType(值,typeof(T),CultureInfo.InvariantCulture):
违约(T);
}
公共对象GetShadowPropertyValue(对象实体,字符串propertyName)
{
返回此.Entry(entity).Property(propertyName).CurrentValue;
}
private void BeforeSaveTriggers()
{
ValidateEntities()
public override int SaveChanges()
{
    ChangeTracker.DetectChanges();

    foreach (var entry in ChangeTracker.Entries().Where(entity => entity.State == EntityState.Modified))
    {
        entry.Property("Updated").CurrentValue = DateTime.UtcNow;
    }

    return base.SaveChanges();
}
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1"/>