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