Entity Framework Core 2.0:如何配置一次抽象基类

问题描述 投票:0回答:2

我有一个基本模型:

public abstract class Status
{
     public string updateUserName { get; set; }
}

然后是扩展上面定义的基本模型的模型:

public class Item : Status
{
     public int Id { get; set; }
     public string Description { get; set; }
}

然后我为每个定义了配置类:

public class ItemConfiguration : IEntityTypeConfiguration<Item>
{
    public void Configure(EntityTypeBuilder<Item> builder)
    {
        builder.ToTable("Item", "dbo").HasKey(c => c.Id);
        builder.Property(c => c.Description).IsRequired().HasMaxLength(100);
    }
}

public class StatusConfiguration : IEntityTypeConfiguration<Status>
{
    public void Configure(EntityTypeBuilder<Status> builder)
    {
        builder.Property(c => c.updateUserName).IsRequired().HasMaxLength(50);
    }

现在,我有以下 Context 类:

public class TestDbContext : DbContext
{
    public TestDbContext(DbContextOptions<TestDbContext> options) : base(options)
    {
    }

    public DbSet<Item> Item { get; set; }

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

我试图弄清楚如何将 StatusConfiguration 类中定义的 Status 模型配置应用到扩展到它的所有模型(本例中只有一个:Item)。我想避免每次使用时都定义相同的 Status 模型配置。 Status 模型本质上是与每个 Item 记录关联的元数据(即数据库中的一个 Item 表包含两个模型中定义的所有属性;仅此而已)。

例如,我当前的实现是以下 ItemConfiguration 类,而不使用 StatusConfiguration 类:

public class ItemConfiguration : IEntityTypeConfiguration<Item>
{
    public void Configure(EntityTypeBuilder<Item> builder)
    {
        builder.ToTable("Item", "dbo").HasKey(c => c.Id);
        builder.Property(c => c.Description).IsRequired().HasMaxLength(100);
        builder.Property(c => c.updateUserName).IsRequired().HasMaxLength(50);
    }
}

当前实现工作正常并按预期迁移到数据库。我只是在寻找一种更易于管理的前进方式。

我的假设是我可以扩展 ItemConfiguration 类以包含 StatusConfiguration 类,但无法在线找到该方法的示例。我希望有更多经验的人可以指出我正确的方向吗?

请告诉我其他信息是否有帮助。

sql-server ef-code-first entity-framework-core asp.net-core-2.0 ef-fluent-api
2个回答
57
投票

如果我理解正确的话,

Status
只是一个基类,而不是参与数据库继承的基实体

在这种情况下,重要的是永远不要直接在实体模型和配置中引用

Status
类,即没有
DbSet<Status>
,没有
Status
ICollection<Status>
类型的导航属性,没有
modelBuilder.Entity<Status>()
调用,也没有
IEntityTypeConfiguration<Status>
.

相反,您始终必须引用继承自

Status
的具体类型。为了重用配置代码,您应该使用constrained generic方法或类并传递具体的实体类型。

既然您正在使用

IEntityTypeConfiguration
类,最自然的可能就是使您的
StatusConfiguration
类通用:

public class StatusConfiguration<TEntity> : IEntityTypeConfiguration<TEntity>
    where TEntity : Status
{
    public virtual void Configure(EntityTypeBuilder<TEntity> builder)
    {
        builder.Property(c => c.updateUserName).IsRequired().HasMaxLength(50);
    }
}

并让派生实体配置类从中派生:

public class ItemConfiguration : StatusConfiguration<Item>
{
    public override void Configure(EntityTypeBuilder<Item> builder)
    {
        base.Configure(builder); // <--
        builder.ToTable("Item", "dbo").HasKey(c => c.Id);
        builder.Property(c => c.Description).IsRequired().HasMaxLength(100);
    }
}

0
投票

您可以使用 OnModelCreating 方法中的反射和基类的通用 Configuration 类来完成此操作,而无需为每个实体调用 base.Configure(builder) :

public class StatusConfiguration<TEntity> : IEntityTypeConfiguration<TEntity>
    where TEntity : Status
{
    public virtual void Configure(EntityTypeBuilder<TEntity> builder)
    {
        builder.Property(c => c.updateUserName).IsRequired().HasMaxLength(50);
    }
}

在 OnModel 创建中找到继承基类的类,并创建一个配置实例,如下所示:

      var subTypes= Assembly
           .GetAssembly(typeof(Status))
           .GetTypes()
           .Where(c => c.IsSubclassOf(typeof(Status)));
        foreach (var item in subTypes)
        {
            Type baseEntityConfigGenericType= typeof(StatusConfiguration<>);
            Type[] typeArgs = { item };
            Type constructed = baseEntityConfigGenericType.MakeGenericType(typeArgs);
            dynamic o = Activator.CreateInstance(constructed);
            if (builder.Model.FindEntityType(item) != null)
                builder.ApplyConfiguration(o);
}

现在,每次添加新的子类型时,在迁移过程中,它都会自动考虑基本类型规范。

© www.soinside.com 2019 - 2024. All rights reserved.