索引

索引是许多数据存储中的常见概念。 尽管它们在数据存储中的实现可能会有所不同,但它们可用于使基于列(或一组列)的查找更加高效。 有关良好索引使用的详细信息,请参阅性能文档中的索引部分

可对列指定索引,如下所示:

[Index(nameof(Url))]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

注意

根据约定,会在用作外键的每个属性(或属性集)中创建索引。

复合索引

索引还可以跨多个列:

[Index(nameof(FirstName), nameof(LastName))]
public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

多个列上的索引(也称为复合索引),可以加快对索引列进行筛选的查询速度,还可以加快仅对索引覆盖的第一列进行筛选的查询速度。 有关详细信息,请参阅性能文档

索引唯一性

默认情况下,索引不唯一:对于索引的列集,允许多行具有相同的值。 可使索引唯一,如下所示:

[Index(nameof(Url), IsUnique = true)]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

尝试为索引的列集插入多个具有相同值的实体将导致引发异常。

索引排序顺序

注意

EF Core 7.0 中引入了此功能。

在大多数数据库中,索引涵盖的每个列可以是升序或降序。 对于仅涵盖一列的索引,这通常无关紧要:数据库可以根据需要按相反顺序遍历索引。 但是,对于复合索引,排序对于良好的性能至关重要,并且可以表示查询是否使用索引之间的差异。 通常,索引列的排序顺序应对应于查询的 ORDER BY 子句中指定的排序顺序。

索引排序顺序默认为升序。 可以按如下所示使所有列按降序排列:

[Index(nameof(Url), nameof(Rating), AllDescending = true)]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public int Rating { get; set; }
}

还可以按列指定排序顺序,如下所示:

[Index(nameof(Url), nameof(Rating), IsDescending = new[] { false, true })]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public int Rating { get; set; }
}

索引命名和多个索引

根据约定,在关系数据库中创建的索引被命名为 IX_<type name>_<property name>。 对于复合索引,<property name> 将成为以下划线分隔的属性名称列表。

可设置在数据库中创建的索引的名称:

[Index(nameof(Url), Name = "Index_Url")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

请注意,如果在同一组属性上多次调用 HasIndex,则会继续配置单个索引而不是创建一个新索引:

modelBuilder.Entity<Blog>()
    .HasIndex(b => new { b.FirstName, b.LastName })
    .HasDatabaseName("IX_Names_Ascending");

modelBuilder.Entity<Blog>()
    .HasIndex(b => new { b.FirstName, b.LastName })
    .HasDatabaseName("IX_Names_Descending")
    .IsDescending();

由于第二次调用 HasIndex 会覆盖第一次调用,因此只会创建一个降序索引。 这对于进一步配置按约定创建的索引非常有用。

要在同一组属性上创建多个索引,请将一个名称传递给 HasIndex,该名称将用于在 EF 模型中标识索引,并将该索引与同一组属性上的其他索引区分开来:

modelBuilder.Entity<Blog>()
    .HasIndex(b => new { b.FirstName, b.LastName }, "IX_Names_Ascending");

modelBuilder.Entity<Blog>()
    .HasIndex(b => new { b.FirstName, b.LastName }, "IX_Names_Descending")
    .IsDescending();

请注意,此名称也用作数据库名称的默认值,因此,显式调用 HasDatabaseName 不是必需的。

索引筛选器

通过某些关系数据库,可指定筛选索引或部分索引。 这使你可以仅索引列值的子集,从而减少索引的大小并改善性能和磁盘空间的使用情况。 有关 SQL Server 筛选索引的详细信息,请参阅文档

可使用 Fluent API 在索引上指定筛选器(以 SQL 表达式的形式提供):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasIndex(b => b.Url)
        .HasFilter("[Url] IS NOT NULL");
}

使用 SQL Server 提供程序时,EF 为作为唯一索引一部分的所有可为 null 列添加 'IS NOT NULL' 筛选器。 若要替代此约定,可提供一个 null 值。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasIndex(b => b.Url)
        .IsUnique()
        .HasFilter(null);
}

包含列

通过某些关系数据库,可配置一组列,这些列包含在索引中,但不是其“键”的一部分。 当查询中的所有列都作为键列或非键列包含在索引中时,这可以显著提高查询性能,因为无需访问表本身。 有关 SQL Server 包含的列的详细信息,请参阅文档

在以下示例中,Url 列是索引键的一部分,因此对该列的任何查询筛选都可以使用索引。 但除此之外,仅访问 TitlePublishedOn 列的查询将不需要访问表,并且会更高效地运行:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasIndex(p => p.Url)
        .IncludeProperties(
            p => new { p.Title, p.PublishedOn });
}

检查约束

检查约束是一项标准关系功能,让你可以定义一个条件,该条件必须适用于表中的所有行;任何违反约束的插入或修改数据的尝试都将失败。 检查约束类似于非 null 约束(禁止列中的空值)或唯一约束(禁止重复),但允许定义任意 SQL 表达式。

可使用 Fluent API 指定表的检查约束(以 SQL 表达式的形式提供):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Product>()
        .ToTable(b => b.HasCheckConstraint("CK_Prices", "[Price] > [DiscountedPrice]"));
}

可在同一个表上定义多个检查约束,每个约束都有自己的名称。

注意:一些常见的检查约束可通过社区包 EFCore.CheckConstraints 进行配置。