索引

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

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

[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; }
}

尝试插入具有相同值的多个实体到索引的列集合中,将导致抛出异常。

索引排序顺序

在大多数数据库中,索引涵盖的每个列都可以升序或降序。 对于仅覆盖一列的索引,这通常无关紧要:数据库可以根据需要按相反顺序遍历索引。 但是,对于复合索引,排序对于确保良好的性能至关重要,并且可能决定查询是否使用该索引。 通常,索引列的排序顺序应与查询子句中指定的 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<Person>()
    .HasIndex(p => new { p.FirstName, p.LastName })
    .HasDatabaseName("IX_Names_Ascending");

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

由于第二 HasIndex 次调用重写第一个调用,因此只创建一个降序索引。 这可用于进一步配置按约定创建的索引。

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

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

modelBuilder.Entity<Person>()
    .HasIndex(p => new { p.FirstName, p.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 });
}

CHECK 约束

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

可以使用 Fluent API 指定表的检查约束,作为 SQL 表达式提供:

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

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

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