索引是许多数据存储的常见概念。 尽管数据存储中的实现可能有所不同,但它们用于提高基于列(或列集)的查找效率。 有关良好的索引使用情况的详细信息,请参阅性能文档中的 “索引”部分 。
可对列指定索引,如下所示:
[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
该列是索引键的一部分,因此对该列进行的任何查询筛选都可以使用该索引。 但是,仅访问Title
和PublishedOn
列的查询不需要访问表,因此将更有效地运行。
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 配置一些常见的检查约束。