实体框架过滤器索引
Posted
技术标签:
【中文标题】实体框架过滤器索引【英文标题】:Entity Framework Filter Index 【发布时间】:2015-12-29 22:03:18 【问题描述】:我使用 EF 6.1.x 代码优先。
我了解到 EF latest 不支持带有过滤器表达式的索引。
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))
【问题讨论】:
趣味文章:***.com/questions/29922099/… Official EntityFramework documentation for Index filters. 【参考方案1】:在 EF 6.1 中,使用 Code First 和 DbMigrations 的工作方式是在 DbMigration
类中使用 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,但使用过滤器 - 答案是“否”
几乎是重复的:Entity Framework 6.1 - Create index with INCLUDE statement
【讨论】:
是的,我正在寻找 blog.oneunicorn.com/2014/02/15/… 但我知道实际上它还不可能。还是谢谢! 我已经添加了这个部分类并做了“update-database”然后所有迁移都被明确应用但是这个新索引没有在数据库中创建??? 好吧,我先创建了一个空的迁移,然后我得到这个错误:错误号:102,状态:1,类:15 ')' 附近的语法不正确。请您更正“)”错误吗? 糟糕,我在您的 sql 中复制了没有删除导致语法错误的杂散逗号。编辑修复它。【参考方案2】:请注意,现在 EF core 2.1.X 通过 HasFilter
扩展在 IndexBuilder
上添加了对过滤索引的内置支持,因此不再需要自定义实现。
详情请见this
【讨论】:
【参考方案3】:我知道原帖指的是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 保持不变。
【讨论】:
以上是关于实体框架过滤器索引的主要内容,如果未能解决你的问题,请参考以下文章