防止EF Code First继承TPT中的索引

时间:2016-01-14 19:40:52

标签: c# entity-framework-6

我们有一个代码优先的模型,看起来像这样。

Code First Model

SuitabilityCheck定义如下:

public abstract class SuitabilityCheck : ISharded
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Index("IX_SuitabilityCheck_ShardKey_Id", IsUnique = true, Order = 2)]
    public int Id
    { get; set; }

    [Index("IX_SuitabilityCheck_ShardKey_Id", IsUnique = true, Order = 1)]
    public Guid ShardKey
    { get; set; }
}

当我们生成迁移时,Id列上定义的索引部分会通过层次结构传播,因此层次结构的每个级别都会获得一个或多个其他索引。

BackgroundSuitabilityCheck会创建以下附加索引,因为当Id列向下传播时,索引部分就会被声明。

.Index(t => t.Id, unique: true, name: "IX_SuitabilityCheck_ShardKey_Id")

RecurringBackgroundSuitabilityCheck创建了两个额外的索引,一个来自其直接父级(即BackgroundSuitabilityCheck),另一个来自其父级的父级(即SuitabilityCheck)。

.Index(t => t.Id, unique: true, name: "IX_SuitabilityCheck_ShardKey_Id")
.Index(t => t.Id, name: "IX_BackgroundSuitabilityCheck_ShardKey_Id")

有没有人知道是否有办法阻止EF在层次结构中传播此索引?

我知道我们可以使用类似迁移配置的Seed方法来删除我们不想要的索引,但是数据库与EF认为存在的模型不匹配,这可能导致阻抗不匹配路。我宁愿告诉EF不要将索引级联到层次结构中。

1 个答案:

答案 0 :(得分:0)

我的答案的一个方面是,防止为主键列上的外键创建自动索引。另一个与问题示例中索引的组成有关。

避免创建ForeignKey索引

不幸的是,我知道无法告诉EF,自动在一个特定属性上创建索引。可以在迁移代码的UpDown方法中删除索引创建语句。一种不同的方法是,通常禁用在外键上自动创建索引,这可以通过System.Data.Entity.ModelConfiguration.Conventions.ForeignKeyIndexConvention实现:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
}

这解决了在没有显式索引注释的情况下的问题。权衡的是,任何实际需要的外键索引现在都需要明确地添加(至少可以这样做,与删除计划索引不同)。

<强>后续

经过一些实验后,我意识到,ForeignKeyIndexConvention是开始使用更细粒度规则的理想基础:导出自定义约定,覆盖Apply函数并有选择地调用base.Apply用于需要自动创建ForeignKey索引的情况的函数。根据经验,对于任意对多关系,FK上的索引应该是合理的,而一对一或零/一对一关系通常涉及PK / FK组合,其中不需要额外的索引:

public class ForeignKeyNotPkIndexConvention : ForeignKeyIndexConvention
{
    public override void Apply(AssociationType item, DbModel model)
    {
        if (item.AssociationEndMembers.Any(x => x.RelationshipMultiplicity == RelationshipMultiplicity.Many))
            base.Apply(item, model);
    }
}

添加此约定而不是原始ForeignKeyIndexConvention应该处理大多数(所有?)情况,其中FK不是PK。

避免在子类中使用Explicitely配置的索引

但是,即使没有ForeignKeyIndexConvention,明确注释的索引也会通过层次结构传播。这可以通过不明确地注释Id列上的索引来解决。在您的情况下,这意味着,ShardKey属性上只有一个非唯一索引。这是有道理的,因为PK Id无论如何都被编入索引,所以就可访问性而言,它不需要作为复合索引的第二个元素,并且就唯一性而言,即使没有ShardKey它也是唯一的,所以具有复合唯一性是多余的。

<强>替代

如果您有充分的理由将ID列保留为复合索引的一部分,则可以选择通过流畅的api重新映射整个表。这样,索引保持映射表的本地,而不是在继承层次结构中扩展

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
    modelBuilder.Entity<SuitabilityCheck>().HasKey(x => x.Id)
        .Map(x =>
        {
            x.Property(y => y.ShardKey).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_SuitabilityCheck_ShardKey_Id", 1) { IsUnique = true }));
            x.Property(y => y.Id).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_SuitabilityCheck_ShardKey_Id", 2)));
            // Mention all other columns of SuitabilityCheck, otherwise the class is split into multiple tables
        });
}

如评论所述,Map命令通常允许您拆分表映射,以便在重新映射中忘记任何属性时实际发生的事情。