如何阻止 EF Core 索引所有外键

How to stop EF Core from indexing all foreign keys

等问题中所述,EF Core 似乎会自动为每个外键生成索引。这对我来说是一个合理的默认值(我们不要在这里发表意见 war...),但在某些情况下它只是浪费 space 并减慢插入和更新速度。我该如何根据具体情况进行预防?

我不想完全关闭它,因为它利大于弊;我不想为我 do 想要的所有那些索引手动配置它。我只是想在 特定的 FKs 上阻止它。

相关问题:EF 文档中是否提到了这些索引是自动创建的?我在任何地方都找不到它,这可能就是为什么我找不到如何禁用它的原因?

肯定有人会质疑我为什么要这样做...所以为了节省时间,链接问题的OPer在评论中给出了一个很好的例子:

We have a People table and an Addresses table, for example. The People.AddressID FK was Indexed by EF but I only ever start from a People row and search for the Addresses record; I never find an Addresses row and then search the People.AddressID column for a matching record.

如果确实有必要避免使用某些外键索引 - 据我所知(目前) - 在 .Net Core 中,有必要删除将在生成的迁移代码文件中设置索引的代码.

另一种方法是结合属性或可能避免创建索引的扩展方法来实现自定义迁移生成器。您可以在 EF6 的这个答案中找到更多信息:. But I'm not sure if it will work in .Net Core too. The approach seems to be bit different, here is a MS doc article 应该有所帮助。

但是,我强烈建议不要这样做!我反对这样做,因为您必须修改生成的迁移文件,而不是因为 FK 没有使用索引。就像您在问题评论中提到的那样,在现实世界中,某些情况下需要这种方法。


对于其他人,他们不确定是否必须避免在 FK 上使用索引,因此他们必须修改迁移文件:

在你这样做之前,我建议在 FK 上使用索引来实现应用程序,并检查性能和 space 使用情况。因此我会产生很多测试数据。 如果在测试或 QA 阶段确实导致性能和 space 使用问题,仍然可以删除迁移文件中的索引。

因为我们已经在此处讨论了 EnsureCreatedmigrations 的完整性有关 EnsureCreated 和迁移的更多信息(即使您不需要它:-)):

EF Core 有一个配置选项来替换它的一项服务。

我发现将 IConventionSetBuilder 替换为自定义的方法会更简洁。

https://giridharprakash.me/2020/02/12/entity-framework-core-override-conventions/

Entity Framework core 2.0(提出问题时可用的最新版本)没有这样的机制,但 EF Core 2.2 可能 - 以 Owned Entity Types.[= 的形式43=]

也就是说,既然你说:

" I only ever start from a People row and search for the Addresses record; I never find an Addresses row"

那么您可能希望将 Address 设为 拥有的实体类型 (尤其是具有 'Storing owned types in separate tables' 的变体,以匹配您选择的存储地址信息在单独的 Addresses table 中)。 该功能的文档似乎说匹配:

"Owned entities are essentially a part of the owner and cannot exist without it"

顺便说一下,既然该功能在 EF 中,这可以解释为什么 EF 总是为 HasMany/HasOne 创建索引。这可能是因为 Has* 关系旨在用于其他实体(而不是 'value objects')和这些,因为它们有自己的身份, 意味着独立查询并允许访问与使用导航属性相关的其他实体。对于这样的用例,在没有索引的情况下使用这样的导航属性是非常危险的(一些查询可能会使数据库速度大大降低)。

这里有一些注意事项:

将一个实体变成一个拥有的实体并不仅仅指示 EF 关于索引,而是指示以一种有点不同的方式将模型映射到数据库(下面有更多内容)但最终效果实际上没有 People.

上的额外索引

但很有可能,这实际上可能是对您来说 更好的 解决方案:这样您也可以说没有人应该查询地址(通过 not allowing to create a DbSet<T> of that type),通过这些昂贵的无索引查询,最大限度地减少有人使用它来访问其他实体的机会。

至于区别是什么,您会注意到,如果您让 Person 拥有 Address,EF 将在 [=13= 中创建一个 PersonId 列] table,与People table中的AddressId不同(从某种意义上说,缺少外键有点作弊:用于查询的索引Person from Address 在那里,只是它是 People table 的主键索引,无论如何它都在那里)。但是请注意,这种设计实际上是相当不错的 - 它不仅需要少一列(在 People 中没有 AddressId),而且它还保证无法创建孤立的 Address 记录您的代码将永远无法访问。

如果您仍想保留 Addresses 中的 AddressId 列,那么还有一个选择:

  • 只需为 Addresses table 中的外键选择一个名称 AddressId 并“假装”您不知道它恰好具有相同的值PersonId :)

如果该选项不好笑(例如,因为您无法更改数据库模式),那么您有点不走运。但请注意,在 Current shortcomings of EF 中,他们仍然列出 “拥有的实体类型的实例不能由多个所有者共享”,而以前版本的一些缺点已经列为解决。 space 可能值得关注,因为在我看来,解决这个问题可能涉及引入在 People 中拥有 AddressId 的能力,因为在这样的模型中,对于拥有的对象要在许多实体之间共享,外键需要与拥有的实体坐在一起,以便为每个实体创建到相同值的关联。

在 OnModelCreating 覆盖中 调用

之后
base.OnModelCreating(modelBuilder);

添加:

var indexForRemoval = modelBuilder.Entity<You_Table_Entity>().HasIndex(x => x.Column_Index_Is_On).Metadata;
modelBuilder.Entity<You_Table_Entity>().Metadata.RemoveIndex(indexForRemoval);

'''