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, который позволяет следующее:
- Сортировать столбцы в вашем индексе
ASC
или DESC
- Разрешить использование ключевого слова
WHERE
Чтобы использовать его, вы должны настроить только индексное имя. Название разделено на 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
который можно расширить.