我知道原帖指的是6.1版本的EF,但经过一番研究,我找到了一种方法,将过滤索引的扩展方法添加到EF Core(1.1版本)的fluent api中强>。也许有人会发现这很有用(也许在旧版本中也有一种方法可以实现)。
不过我必须警告你。由于此解决方案使用 Microsoft.EntityFrameworkCore.Migrations.Internal 和 Microsoft.EntityFrameworkCore.Infrastructure 命名空间中的类,因此无法保证此代码在 EF 更新后可以正常工作。这些命名空间中每个类的摘要中都包含一条消息,说明
此 API 可能会在未来的版本中更改或删除
,所以你已经被警告了。
但切中要害。
首先您必须为IndexBuilder 创建一个标准扩展方法。它的主要职责是为构建的索引添加一个带有条件的新注释。之后将使用 fluent 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 commad 后,我们将看到如下生成的 SQL:
CREATE UNIQUE INDEX [IX_Activities_Identity] ON [Activities] ([Identity]) WHERE [End] IS NULL;
这个方法实际上可以用于将任何自定义 SQL 生成器包含到 ef core fluent api 中。至少只要 EF API 保持不变。