Entity Framework 过滤器索引
Entity Framework Filter Index
我使用 EF 6。1.x Code First。
我了解到最新的 EF 不支持带有过滤器表达式的索引。
SO也没有解决方案:
EF 6.1 Unique Nullable Index
一年后,使过滤器索引与 Code First 和 DbMigrations 一起工作的工作方式是什么?
CREATE UNIQUE NONCLUSTERED INDEX [IX_DefaultLanguageApplicationId] ON [dbo].[Languages]
(
[IsDefaultLanguage] ASC,
[ApplicationId] ASC,
)
WHERE ([IsDefaultLanguage]=(1))
在 EF 6.1 中,使用 Code First 和 DbMigrations 的工作方法是使用 DbMigration
class:
中的 Sql
方法
public partial class AddIndexes : DbMigration
{
public override void Up()
{
Sql(@"CREATE UNIQUE NONCLUSTERED INDEX
[IX_DefaultLanguageApplicationId] ON [dbo].[Languages]
(
[IsDefaultLanguage] ASC,
[ApplicationId] ASC
)
WHERE ([IsDefaultLanguage]=(1))");
}
public override void Down()
{
DropIndex("dbo.Languages", "IX_DefaultLanguageApplicationId");
}
}
但我知道你可能在问你是否可以 create an index using the IndexAttribute introduced in 6.1,但是使用过滤器 - 答案是 "No"
几乎重复:Entity Framework 6.1 - Create index with INCLUDE statement
我知道原来的post指的是EF的6.1版本,但经过一番研究,我找到了一种方法,可以将过滤索引的扩展方法添加到[的流畅api中=54=]EF Core(1.1 版本)。也许有人会发现这很有用(也许在旧版本中也有实现它的方法)。
不过我得警告你。由于此解决方案使用 Microsoft.EntityFrameworkCore.Migrations.Internal
和 Microsoft.EntityFrameworkCore.Infrastructure
命名空间中的 classes,因此无法保证此代码在 EF 更新后仍然有效。这些命名空间中每个 class 的摘要中都包含一条消息,说
This API may change or be removed in future releases
,所以你被警告了。
但是切中要点。
首先你必须为 IndexBuilder
创建一个标准的扩展方法。它的主要职责是将带有条件的新注释添加到构造的索引中。以后用流利的api就用这个方法。以免调用我们的注释 SqlServer:FilteredIndex
.
static class FilteredIndexExtension
{
public static IndexBuilder Filtered(this IndexBuilder indexBuilder, string condition)
{
indexBuilder.HasAnnotation("SqlServer:FilteredIndex", condition);
return indexBuilder;
}
}
接下来您必须允许此注释实际包含在迁移中。您必须为索引构建器覆盖 SqlServerMigrationsAnnotationProvider
的默认行为。
class ExtendedSqlServerMigrationsAnnotationProvider : SqlServerMigrationsAnnotationProvider
{
public override IEnumerable<IAnnotation> For(IIndex index)
{
var baseAnnotations = base.For(index);
var customAnnotatinos = index.GetAnnotations().Where(a => a.Name == "SqlServer:FilteredIndex");
return baseAnnotations.Concat(customAnnotatinos);
}
}
现在是最困难的部分。我们必须覆盖 SqlServerMigrationsSqlGenerator
关于索引的默认行为。
class ExtendedSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
public ExtendedSqlServerMigrationsSqlGenerator(IRelationalCommandBuilderFactory commandBuilderFactory, ISqlGenerationHelper sqlGenerationHelper, IRelationalTypeMapper typeMapper, IRelationalAnnotationProvider annotations, IMigrationsAnnotationProvider migrationsAnnotations) : base(commandBuilderFactory, sqlGenerationHelper, typeMapper, annotations, migrationsAnnotations)
{
}
protected override void Generate(CreateIndexOperation operation, IModel model, MigrationCommandListBuilder builder, bool terminate)
{
base.Generate(operation, model, builder, false);
var filteredIndexCondition = operation.FindAnnotation("SqlServer:FilteredIndex");
if (filteredIndexCondition != null)
builder.Append($" WHERE {filteredIndexCondition.Value}");
if (terminate)
{
builder.AppendLine(SqlGenerationHelper.StatementTerminator);
EndStatement(builder);
}
}
}
如您所见,我们在这里调用了基础生成器,因此我们的条件将添加到它的末尾而不改变它。我们必须记住不要在这里终止基础 SQL 语句(传递给 base.Generate
方法的最后一个参数是 false
)。如果我们的注释被设置,我们可以在 SQL 语句末尾的 WHERE
子句之后附加它的值。之后,根据传递给此方法的参数,我们最终可以终止语句或保持原样。
为了使所有这些部分正常工作,我们必须通过覆盖 DbContext
.
的 OnConfiguring
方法,用新版本替换旧服务。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<SqlServerMigrationsAnnotationProvider, ExtendedSqlServerMigrationsAnnotationProvider>();
optionsBuilder.ReplaceService<SqlServerMigrationsSqlGenerator, ExtendedSqlServerMigrationsSqlGenerator>();
}
现在我们可以像这样使用我们的扩展方法:
builder.HasIndex(a => a.Identity).IsUnique().Filtered("[End] IS NULL");
它将生成这样的迁移:
migrationBuilder.CreateIndex(
name: "IX_Activities_Identity",
table: "Activities",
column: "Identity",
unique: true)
.Annotation("SqlServer:FilteredIndex", "[End] IS NULL");
在程序包管理器控制台中调用 Script-Migration
命令后,我们将看到结果 SQL 如下:
CREATE UNIQUE INDEX [IX_Activities_Identity] ON [Activities] ([Identity]) WHERE [End] IS NULL;
此方法实际上可用于将任何自定义 SQL 生成器包含到 ef core fluent api 中。至少只要 EF API 保持不变。
请注意,现在 EF core 2.1.X 通过 IndexBuilder
上的 HasFilter
扩展添加了对过滤索引的内置支持,因此不再需要自定义实现.
有关详细信息,请参阅 this
我使用 EF 6。1.x Code First。
我了解到最新的 EF 不支持带有过滤器表达式的索引。
SO也没有解决方案:
EF 6.1 Unique Nullable Index
一年后,使过滤器索引与 Code First 和 DbMigrations 一起工作的工作方式是什么?
CREATE UNIQUE NONCLUSTERED INDEX [IX_DefaultLanguageApplicationId] ON [dbo].[Languages]
(
[IsDefaultLanguage] ASC,
[ApplicationId] ASC,
)
WHERE ([IsDefaultLanguage]=(1))
在 EF 6.1 中,使用 Code First 和 DbMigrations 的工作方法是使用 DbMigration
class:
Sql
方法
public partial class AddIndexes : DbMigration
{
public override void Up()
{
Sql(@"CREATE UNIQUE NONCLUSTERED INDEX
[IX_DefaultLanguageApplicationId] ON [dbo].[Languages]
(
[IsDefaultLanguage] ASC,
[ApplicationId] ASC
)
WHERE ([IsDefaultLanguage]=(1))");
}
public override void Down()
{
DropIndex("dbo.Languages", "IX_DefaultLanguageApplicationId");
}
}
但我知道你可能在问你是否可以 create an index using the IndexAttribute introduced in 6.1,但是使用过滤器 - 答案是 "No"
几乎重复:Entity Framework 6.1 - Create index with INCLUDE statement
我知道原来的post指的是EF的6.1版本,但经过一番研究,我找到了一种方法,可以将过滤索引的扩展方法添加到[的流畅api中=54=]EF Core(1.1 版本)。也许有人会发现这很有用(也许在旧版本中也有实现它的方法)。
不过我得警告你。由于此解决方案使用 Microsoft.EntityFrameworkCore.Migrations.Internal
和 Microsoft.EntityFrameworkCore.Infrastructure
命名空间中的 classes,因此无法保证此代码在 EF 更新后仍然有效。这些命名空间中每个 class 的摘要中都包含一条消息,说
This API may change or be removed in future releases
,所以你被警告了。
但是切中要点。
首先你必须为 IndexBuilder
创建一个标准的扩展方法。它的主要职责是将带有条件的新注释添加到构造的索引中。以后用流利的api就用这个方法。以免调用我们的注释 SqlServer:FilteredIndex
.
static class FilteredIndexExtension
{
public static IndexBuilder Filtered(this IndexBuilder indexBuilder, string condition)
{
indexBuilder.HasAnnotation("SqlServer:FilteredIndex", condition);
return indexBuilder;
}
}
接下来您必须允许此注释实际包含在迁移中。您必须为索引构建器覆盖 SqlServerMigrationsAnnotationProvider
的默认行为。
class ExtendedSqlServerMigrationsAnnotationProvider : SqlServerMigrationsAnnotationProvider
{
public override IEnumerable<IAnnotation> For(IIndex index)
{
var baseAnnotations = base.For(index);
var customAnnotatinos = index.GetAnnotations().Where(a => a.Name == "SqlServer:FilteredIndex");
return baseAnnotations.Concat(customAnnotatinos);
}
}
现在是最困难的部分。我们必须覆盖 SqlServerMigrationsSqlGenerator
关于索引的默认行为。
class ExtendedSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
public ExtendedSqlServerMigrationsSqlGenerator(IRelationalCommandBuilderFactory commandBuilderFactory, ISqlGenerationHelper sqlGenerationHelper, IRelationalTypeMapper typeMapper, IRelationalAnnotationProvider annotations, IMigrationsAnnotationProvider migrationsAnnotations) : base(commandBuilderFactory, sqlGenerationHelper, typeMapper, annotations, migrationsAnnotations)
{
}
protected override void Generate(CreateIndexOperation operation, IModel model, MigrationCommandListBuilder builder, bool terminate)
{
base.Generate(operation, model, builder, false);
var filteredIndexCondition = operation.FindAnnotation("SqlServer:FilteredIndex");
if (filteredIndexCondition != null)
builder.Append($" WHERE {filteredIndexCondition.Value}");
if (terminate)
{
builder.AppendLine(SqlGenerationHelper.StatementTerminator);
EndStatement(builder);
}
}
}
如您所见,我们在这里调用了基础生成器,因此我们的条件将添加到它的末尾而不改变它。我们必须记住不要在这里终止基础 SQL 语句(传递给 base.Generate
方法的最后一个参数是 false
)。如果我们的注释被设置,我们可以在 SQL 语句末尾的 WHERE
子句之后附加它的值。之后,根据传递给此方法的参数,我们最终可以终止语句或保持原样。
为了使所有这些部分正常工作,我们必须通过覆盖 DbContext
.
OnConfiguring
方法,用新版本替换旧服务。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<SqlServerMigrationsAnnotationProvider, ExtendedSqlServerMigrationsAnnotationProvider>();
optionsBuilder.ReplaceService<SqlServerMigrationsSqlGenerator, ExtendedSqlServerMigrationsSqlGenerator>();
}
现在我们可以像这样使用我们的扩展方法:
builder.HasIndex(a => a.Identity).IsUnique().Filtered("[End] IS NULL");
它将生成这样的迁移:
migrationBuilder.CreateIndex(
name: "IX_Activities_Identity",
table: "Activities",
column: "Identity",
unique: true)
.Annotation("SqlServer:FilteredIndex", "[End] IS NULL");
在程序包管理器控制台中调用 Script-Migration
命令后,我们将看到结果 SQL 如下:
CREATE UNIQUE INDEX [IX_Activities_Identity] ON [Activities] ([Identity]) WHERE [End] IS NULL;
此方法实际上可用于将任何自定义 SQL 生成器包含到 ef core fluent api 中。至少只要 EF API 保持不变。
请注意,现在 EF core 2.1.X 通过 IndexBuilder
上的 HasFilter
扩展添加了对过滤索引的内置支持,因此不再需要自定义实现.
有关详细信息,请参阅 this