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"/>