gpt4 book ai didi

c# - 如何将同一列添加到 EF Core 中的所有实体?

转载 作者:太空宇宙 更新时间:2023-11-03 17:29:44 25 4
gpt4 key购买 nike

假设我想向我的所有实体添加一个 IsDeleted 列或一些审计列。我可以创建一个基类,我的所有实体都将从该基类继承,这将解决我的问题,但是我无法指定列的创建顺序,因此我将在我的实体字段之前得到所有审计字段,我不想要。我希望他们在 table 的最后。

在 Entity Framework 的标准版本中,我们可以通过使用指定列顺序的注释来做到这一点。但是,目前 EF 核心不存在这样的事情。

我可以在 OnModelCreating() 方法上使用流畅的 api 来完成,问题是我只知道如何为我的每个实体单独执行它,这意味着我必须为我的每个实体编写相同的代码有。

有什么方法可以对我的所有实体通用地执行此操作?某种 for 循环遍历在我的 dbcontext 上的 DbSets 中注册的所有实体?

最佳答案

您的问题标题是关于向多个实体添加相同的属性。但是,您实际上知道如何实现这一点(使用基本类型),并且您的实际问题是如何确保这些属性在生成的表的列中排在最后。

虽然现在列顺序不应该很重要,但我将展示一个您可能更喜欢的替代方案,而不是基本类型,并且将公共(public)属性放在表的末尾。它利用 shadow properties :

Shadow properties are properties that are not defined in your .NET entity class but are defined for that entity type in the EF Core model.

大多数时候,审计属性在应用程序中不需要太多可见性,所以我认为影子属性正是您所需要的。这是一个例子:

我有两个类:

public class Planet
{
public Planet()
{
Moons = new HashSet<Moon>();
}
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Moon> Moons { get; set; }
}

public class Moon
{
public int ID { get; set; }
public int PlanetID { get; set; }
public string Name { get; set; }
public Planet Planet { get; set; }
}

如您所见:它们没有审计属性,它们是精简的 POCO。 (顺便说一句,为方便起见,我将 IsDeleted 与“审计属性”混为一谈,尽管它不是一个并且可能需要另一种方法)。

也许这就是这里的主要信息:类模型不受审计问题的困扰(单一责任),这都是 EF 的业务。

审计属性被添加为影子属性。由于我们想为每个实体执行此操作,因此我们定义了一个基础 IEntityTypeConfiguration:

public abstract class BaseEntityTypeConfiguration<T> : IEntityTypeConfiguration<T>
where T : class
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.Property<bool>("IsDeleted")
.IsRequired()
.HasDefaultValue(false);
builder.Property<DateTime>("InsertDateTime")
.IsRequired()
.HasDefaultValueSql("SYSDATETIME()")
.ValueGeneratedOnAdd();
builder.Property<DateTime>("UpdateDateTime")
.IsRequired()
.HasDefaultValueSql("SYSDATETIME()")
.ValueGeneratedOnAdd();
}
}

具体的配置是从这个基类派生的:

public class PlanetConfig : BaseEntityTypeConfiguration<Planet>
{
public override void Configure(EntityTypeBuilder<Planet> builder)
{
builder.Property(p => p.ID).ValueGeneratedOnAdd();
// Follows the default convention but added to make a difference :)
builder.HasMany(p => p.Moons)
.WithOne(m => m.Planet)
.IsRequired()
.HasForeignKey(m => m.PlanetID);
base.Configure(builder);
}
}

public class MoonConfig : BaseEntityTypeConfiguration<Moon>
{
public override void Configure(EntityTypeBuilder<Moon> builder)
{
builder.Property(p => p.ID).ValueGeneratedOnAdd();
base.Configure(builder);
}
}

这些应该添加到 OnModelCreating 中的上下文模型中:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new PlanetConfig());
modelBuilder.ApplyConfiguration(new MoonConfig());
}

这将生成在末尾具有列 InsertDateTimeIsDeletedUpdateDateTime 的数据库表(独立于 base.Configure( builder) 被调用,BTW),尽管是按那个顺序(字母顺序)。我想这已经足够接近了。

为了使图片完整,下面是如何在 SaveChanges 覆盖中完全自动设置值:

public override int SaveChanges()
{
foreach(var entry in this.ChangeTracker.Entries()
.Where(e => e.Properties.Any(p => p.Metadata.Name == "UpdateDateTime")
&& e.State != Microsoft.EntityFrameworkCore.EntityState.Added))
{
entry.Property("UpdateDateTime").CurrentValue = DateTime.Now;
}
return base.SaveChanges();
}

小细节:我确保在插入实体时数据库默认设置两个字段(见上文:ValueGeneratedOnAdd(),因此排除添加的实体)所以不会由客户端时钟稍微偏离引起的混淆差异。我假设更新总是会晚一点。

要设置 IsDeleted,您可以将此方法添加到上下文中:

public void MarkForDelete<T>(T entity)
where T : class
{
var entry = this.Entry(entity);
// TODO: check entry.State
if(entry.Properties.Any(p => p.Metadata.Name == "IsDeleted"))
{
entry.Property("IsDeleted").CurrentValue = true;
}
else
{
entry.State = Microsoft.EntityFrameworkCore.EntityState.Deleted;
}
}

...或者求助于将 EntityState.Deleted 转换为 IsDeleted = true 的提议机制之一。

关于c# - 如何将同一列添加到 EF Core 中的所有实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52020107/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com