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();
}
}
从
OnModelCreating
重写可以看出,我正在将影子属性 Updated/Created 添加到 Person
实体中。我设置这些属性以填充 SQL 默认值:
- 当添加值时,
Created
- 当添加或更新值时,
Updated
var personId = Guid.Parse("CF5EE27D-C694-408A-9F7B-080FF6315843");
using (var dbContext = new PersonDbContext())
{
var person = new Person
{
Id = personId,
Name = "New Person"
};
dbContext.Add(person);
await dbContext.SaveChangesAsync();
}
using (var dbContext = new PersonDbContext())
{
var person = dbContext.Persons.Find(personId);
var personName = person.Name;
person.Name = $"{personName} {DateTime.UtcNow}";
dbContext.SaveChanges();
}
我可以确认,在插入新人员时,两个属性均设置为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()(请参见默认值)以为新行生成值。然后,您可以使用数据库触发器在更新期间生成值(例如以下示例触发器)。
那么,如果它的行为类似于ValueGeneratedOnAdd()
并且我必须手动干预(创建触发器)来设置此属性,那么ValueGeneratedOnAddOrUpdate()
的目的是什么?
确实,如果我更改Updated
影子属性的定义为
modelBuilder
.Entity<Person>()
.Property<DateTime>("Updated")
.HasDefaultValueSql("GETUTCDATE()")
.ValueGeneratedOnAdd();
在
PersonDbContext
上覆盖SaveChanges
方法。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();
}
这做了预期的事情。
因此问题是 - 在 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"/>