【问题标题】:How to stop EF Core from indexing all foreign keys如何阻止 EF Core 索引所有外键
【发布时间】:2022-04-15 14:08:58
【问题描述】:

正如Entity Framework Indexing ALL foreign key columns 等问题中所述,EF Core 似乎会自动为每个外键生成一个索引。这对我来说是一个合理的默认设置(我们不要在这里陷入舆论战......),但在某些情况下它只是浪费空间并减慢插入和更新速度。如何根据具体情况进行预防?

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

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

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

例如,我们有一个 People 表和一个 Addresses 表。这 People.AddressID FK 已被 EF 索引,但我只从 a People 行并搜索 Addresses 记录;我从来没有找到一个 Addresses 行,然后在 People.AddressID 列中搜索 匹配记录。

【问题讨论】:

  • 如果只是针对几个特定的​​表,难道你不能从你的迁移中删除添加索引的代码吗?
  • @Valuator 对不起,我迷路了。什么迁移?当我的应用程序启动时,它通过调用db.Database.EnsureCreated(); 从头开始​​创建数据库。据我了解,索引的创建是在幕后自动发生的。这是 EF Core 2,顺便说一句。
  • 好的,EnsureCreated 与 Migrations 不同,我自己从未使用过它,尽管我只知道两者不兼容。使用迁移,您可以在应用它们之前查看对数据库的基础更改。
  • 此外,我建议阅读以下MS doc about EnsureCreated()。如果您有一些模型更改,它不会更新您的数据库 - 迁移会这样做。也很有趣(即使是 EF7)EF7 EnsureCreated vs. Migrate MethodsHere 你可以找到所有支持方法的 EF 和 EF Core 教程。
  • @pbarranis 好的。好吧,我会使用迁移并自己管理索引,并且会删除不需要的索引。我希望能找到一个可以接受的答案,因为我也对它感兴趣。 :)

标签: entity-framework ef-core-2.0


【解决方案1】:

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

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

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

【讨论】:

  • 工作量很大,但总比没有好。谢谢!
  • 这在 EF Core 中不起作用。关于现在如何完成的任何想法?
【解决方案2】:

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

另一种方法是结合属性或扩展方法来实现自定义迁移生成器,以避免创建索引。您可以在 EF6 的此答案中找到更多信息:EF6 preventing not to create Index on Foreign Key。但我不确定它是否也适用于 .Net Core。方法似乎有点不同,这里有一个 MS doc article 应该会有所帮助。

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


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

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

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

【讨论】:

  • 谢谢。我同意这样做会完全拖累,容易出错,而且只是一个全面的坏主意。我绝对不会走这条路,但我的数据库很小,对性能的影响很小。可悲的是,我花了更多时间研究 如何 来解决这个问题,而不是查看我的整个架构并列出所有不必要的 FK 索引以进行删除(其中可能有 50-100 )。呃。
  • 当前的设计有变化吗?我注意到正在创建的索引设置为唯一的。如果索引是针对外键的,那么默认值不应该是不唯一的吗?
  • @RobWhite 我知道您的评论是旧评论,但也许其他人会发现这很有帮助 - 仅当您的 FK 定义一对一关系时,索引才设置为唯一。
【解决方案3】:

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

也就是说,既然你说:

“我只从People 行开始搜索Addresses 记录;我从未找到Addresses 行”

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

“拥有的实体本质上是拥有者的一部分,没有它就无法存在”

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

这里有一些警告:

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

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

至于有什么区别,你会注意到,如果你将Address设为Person所有,EF会在Address表中创建一个PersonId列,这与你的@987654339不同@ 在People 表中(某种意义上,缺少外键有点作弊:有一个从Address 查询Person 的索引,只是它是People 表的主键索引,它反正在那里)。但请注意,这种设计实际上相当不错 - 它不仅需要少一列(People 中没有 AddressId),而且它还保证没有办法让孤立的 Address 记录您的代码永远不会可以访问。

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

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

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

【讨论】:

    【解决方案4】:

    在 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);
    

    '''

    【讨论】:

      猜你喜欢
      • 2021-05-23
      • 2011-02-27
      • 2018-06-10
      • 1970-01-01
      • 2020-07-24
      • 2022-12-05
      • 2011-04-21
      • 2011-05-21
      • 2019-06-23
      相关资源
      最近更新 更多