在使用Code First的EF 6.1中,您可以使用实体中的属性创建索引,或者使用以下行中的流畅API:
Property(x => x.PropertyName)
.IsOptional()
.HasMaxLength(450)
.HasColumnAnnotation("Index",
new IndexAnnotation(new IndexAttribute("IX_IndexName") {IsUnique = true, }));
有没有什么方法可以像在SQL Server中一样以原样的方式来表示脚手架WHERE PropertyName IS NOT NULL
(参见:https://stackoverflow.com/a/767702/52026)?
我没有找到告诉EF使用where where子句的方法,但这里有一些解决方法。检查它是否适合您的情况。
Up
方法中创建的DbMigration类中,运行sql以创建唯一可为空的索引。码:
// 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));
注意:不要忘记创建降级,以。覆盖Down
方法并使用DropIndex
方法:
DropIndex(tableName, indexName);
如果数据库中已存在可能与唯一索引约束冲突的数据,则可能还需要一些其他代码。
注意:在这里您可以使用CreateIndex方法,但我无法使用它创建正确的索引。 EF只是忽略我的anonymousArguments或者我写错了。您可以自己尝试并在此处写下您的结果。语法如下:
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"
});
5尝试为唯一列和其他相等值添加两个带空值的条目。
这是我的演示代码 - Pastebin
在EF Core中,您可以在Fluent API中使用HasFilter方法来实现您所需的内容,而无需在迁移中添加自定义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");
不,你不能原生地做到这一点。
但我创建了一个自定义SQL生成器,它支持以下功能:
ASC
或DESC
中的列进行排序WHERE
关键字为了能够使用它,您必须仅调整索引名称。该名称由:
分为3部分。部分是:
如果你有2列的索引,需要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
文件,如下所示:code here。
基于Viktor的答案,我提出了自动创建此代码的解决方案。
最终的迁移文件不应该使用CreateIndex
方法,而是我命名为CreateIndexNullable
的方法。我在DbMigrationEx
创建的这个方法扩展了DbMigration
protected void CreateIndexNullable(string table, string column, string name)
{
Sql($@"CREATE UNIQUE NONCLUSTERED INDEX [{name}] ON {table}([{column}] ASC) WHERE([{column}] IS NOT NULL);");
}
如何更改迁移类代码?
在我设置的Migration文件夹中创建的Configuration
类中
CodeGenerator = new CSharpMigrationCodeGeneratorIndexNullable();
我的CSharpMigrationCodeGeneratorIndexNullable
课程扩展CSharpMigrationCodeGenerator
。我不会展示确切的课程内容,我只是提出这个想法。基于CSharpMigrationCodeGenerator
内容我覆盖了一些方法。实体框架项目可在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
属性。