EF 6.1 Уникальный нулевой индекс

В EF 6.1 с использованием Code First вы можете создавать индексы с использованием атрибутов в своих сущностях или используя свободный API по строкам:

 Property(x => x.PropertyName)
                .IsOptional()
                .HasMaxLength(450)
                .HasColumnAnnotation("Index",
                    new IndexAnnotation(new IndexAttribute("IX_IndexName") {IsUnique = true,  }));

Можно ли сказать scaffold WHERE PropertyName IS NOT NULL так же, как в SQL Server изначально (см. qaru.site/info/15713/...)?

Ответ 1

Я не нашел способ сказать EF использовать это предложение where, но здесь есть некоторое обходное решение. Проверьте, подходит ли оно вашему делу.

  • Установить Entity Framework, Определить свой DbContext, сущности, установить строку в app.config и т.д.
  • Включить миграцию - запустить в консоли диспетчера пакетов '-EnableMigration'
  • Создать DbMigration - запустить в консоли диспетчера пакетов "Добавить-миграция MigrationName"
  • В созданном классе DbMigration в ovverided Up метод запустит ваш sql для создания уникального индекса с возможностью NULL.

код:

// Add unique nullable index 
string indexName = "IX_UQ_UniqueColumn";
string tableName = "dbo.ExampleClasses";
string columnName = "UniqueColumn";

Sql(string.Format(@"
    CREATE UNIQUE NONCLUSTERED INDEX {0}
    ON {1}({2}) 
    WHERE {2} IS NOT NULL;",
    indexName, tableName, columnName));

Примечание. Не забудьте также создать понижение. Ovveride Down и используйте метод DropIndex внутри:

DropIndex(tableName, indexName);

Также вам может понадобиться дополнительный код, если в базе данных уже есть данные, которые могут конфликтовать с уникальным ограничением индекса.

ПРИМЕЧАНИЕ. Здесь вы можете использовать метод CreateIndex, но мне не удалось создать с ним правильный индекс. EF просто игнорирует мои анонимные аргументы или я их неправильно пишу. Вы можете попробовать сами и написать здесь с вашим результатом. Синтаксис следующий:

CreateIndex(
    table: "dbo.ExampleClasses",
    columns: new string[] { "UniqueColumn" },
    unique: true,
    name: "IX_UniqueColumn",
    clustered: false,
    anonymousArguments: new
    {
        Include = new string[] { "UniqueColumn" },
        Where = "UniqueColumn IS NOT NULL"
    });

Забастовкa >  5 Попытайтесь добавить два etries с нулевыми значениями для уникального столбца и других равных значений.

Вот мой демо-код - Pastebin

Ответ 2

В EF Core вы можете использовать метод HasFilter в свободном API, чтобы достичь того, что вы ищете, не добавляя пользовательский SQL в миграцию.

builder.Entity<Table>()
    .HasIndex(x => x.PropertyName)
    .HasName("IX_IndexName")
    .HasFilter("PropertyName IS NOT NULL");

Это создает миграцию как это:

migrationBuilder.CreateIndex(
    name: "IX_IndexName",
    table: "Table",
    columns: new[] { "PropertyName" },
    filter: "PropertyName IS NOT NULL");

Ответ 3

Нет, ты не можешь сделать это изначально.

Но я создал собственный генератор SQL, который позволяет следующее:

  1. Сортировать столбцы в вашем индексе ASC или DESC
  2. Разрешить использование ключевого слова WHERE

Чтобы использовать его, вы должны настроить только индексное имя. Название разделено на 3 части : Части являются:

  1. Название индекса
  2. Сортировать заказы
  3. Где пункт

Если у вас есть индекс по 2 столбцам, Column1 нужно отсортировать Column1 в ASC и Column2 DESC, и вам нужно предложение where, ваше имя индекса будет:

var uniqueName = "UN_MyIndex:ASC,DESC:Column1 IS NOT NULL";

И вы просто используете это так:

Property(t => t.Column1)
            .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute(uniqueName) { IsUnique = true, Order = 1 }));

Property(t => t.Column2)
            .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute(uniqueName) { IsUnique = true, Order = 2 }));

Затем в вашем файле Configuration.cs добавьте эту строку в ваш конструктор:

SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerMigrationSqlGenerator());

Наконец, создайте файл CustomSqlServerMigrationSqlGenerator.cs как показано ниже: код здесь.

Ответ 4

Исходя из ответа Виктора, я придумаю решение, создающее этот код автоматически.

Конечный файл миграции не должен использовать метод CreateIndex а тот, который я назвал CreateIndexNullable. Этот метод я создал в DbMigrationEx который расширяет DbMigration

protected void CreateIndexNullable(string table, string column, string name)
{
    Sql([email protected]"CREATE UNIQUE NONCLUSTERED INDEX [{name}] ON {table}([{column}] ASC) WHERE([{column}] IS NOT NULL);");
}

Как изменить код класса миграции?

В классе Configuration который создается в папке "Миграция", я установил

CodeGenerator = new CSharpMigrationCodeGeneratorIndexNullable();

Мой CSharpMigrationCodeGeneratorIndexNullable класс расширяет CSharpMigrationCodeGenerator. Я не собираюсь показывать точное содержание класса, я просто представлю идею. Основываясь на содержимом CSharpMigrationCodeGenerator я переопределил некоторые методы. Проект Entity Framework доступен по адресу https://github.com/aspnet/EntityFramework6.

Чтобы изменить класс миграции на DbMigrationEx я использовал метод

Generate(IEnumerable<MigrationOperation> operations, string @namespace, string className)

Единственное, что нужно изменить, это

WriteClassStart(
    @namespace, className, writer, "DbMigration", designer: false,
    namespaces: GetNamespaces(operations));

Чтобы изменить метод миграции на CreateIndexNullable я использовал метод

Generate(CreateIndexOperation createIndexOperation, IndentedTextWriter writer)

Вам нужно изменить строку

writer.Write("CreateIndex(");

Также

WriteIndexParameters(createIndexOperation, writer);

в

writer.Write(", ");
writer.Write(Quote(createIndexOperation.Name));

Но как узнать, должен ли индекс быть обнуляемым?

createIndexOperation содержит индексную информацию. Я не смог изменить создание CreateIndexOperation, но его свойств Table, Name и Columns могло быть достаточно, чтобы добраться до полей в классе сущностей и получить атрибут Index который можно расширить.