Индексы

Индексы широко используются во многих хранилищах данных. Хотя их реализации в хранилище данных может различаться, они используются для более эффективного поиска по столбцу (или набору столбцов). Дополнительные сведения о правильном использовании индексов см. в разделе об индексах в документации по производительности.

Индекс для столбца можно указать следующим образом:

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

Примечание

Настройка индексов с помощью заметок к данным появилась в EF Core 5.0.

Примечание

По соглашению индекс создается в каждом свойстве (или наборе свойств), используемом в качестве внешнего ключа.

EF Core поддерживает только один индекс для каждого отдельного набора свойств. Если настроить индекс для набора свойств, для которого уже определен индекс, в соответствии с соглашением или предыдущей конфигурацией, то будет изменено определение этого индекса. Это полезно, если необходимо дополнительно настроить индекс, созданный по соглашению.

Составной индекс

Индекс может также охватывать более одного столбца:

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

При попытке вставить более одной сущности с одинаковыми значениями для набора столбцов индекса возникнет исключение.

Имя индекса

По соглашению, индексы, созданные в реляционной базе данных, называются 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; }
}

Фильтр индекса

Некоторые реляционные базы данных позволяют указать отфильтрованный или частичный индекс. Это позволяет индексировать только часть значений столбца, уменьшая размер индекса, повышая производительность и оптимизируя использование дискового пространства. Дополнительные сведения об отфильтрованных индексах 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 добавляет фильтр 'IS NOT NULL' для всех столбцов, допускающих значение 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>()
        .HasCheckConstraint("CK_Prices", "[Price] > [DiscountedPrice]", c => c.HasName("CK_Product_Prices"));
}

В одной таблице можно определить несколько проверочных ограничений, каждое с отдельным именем.

Примечание. Некоторые распространенные проверочные ограничения можно настроить с помощью пакета от сообщества EFCore.CheckConstraints.