我们有一个代码优先的模型,看起来像这样。
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不要将索引级联到层次结构中。
答案 0 :(得分:0)
我的答案的一个方面是,防止为主键列上的外键创建自动索引。另一个与问题示例中索引的组成有关。
不幸的是,我知道无法告诉EF,不自动在一个特定属性上创建索引。可以在迁移代码的Up
和Down
方法中删除索引创建语句。一种不同的方法是,通常禁用在外键上自动创建索引,这可以通过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。
但是,即使没有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
命令通常允许您拆分表映射,以便在重新映射中忘记任何属性时实际发生的事情。