Wiem, że oryginalny post dotyczył wersji 6.1 EF, ale po pewnych badaniach znalazłem sposób na dodanie metody rozszerzania filtrowanych indeksów do płynnego API z EF Core (wersja 1.1). Może ktoś uzna to za użyteczne (i być może istnieje sposób na wdrożenie tego również w starszych wersjach). Muszę Cię jednak ostrzec. Ponieważ to rozwiązanie wykorzystuje klasy z obszarów nazw Microsoft.EntityFrameworkCore.Migrations.Internal
i Microsoft.EntityFrameworkCore.Infrastructure
, nie ma gwarancji, że ten kod będzie działał po zaktualizowaniu EF. W podsumowaniu każdej klasy w tych przestrzeniach nazw znajduje się masaż informujący, że użytkownik został ostrzeżony.
Ale do rzeczy.
Najpierw należy utworzyć standardową metodę rozszerzenia dla IndexBuilder
. Jego głównym zadaniem będzie dodawanie nowej adnotacji z warunkiem do skonstruowanego indeksu. Jedna z nich zastosuje tę metodę z płynnym api. Aby nie zadzwonić do naszej adnotacji SqlServer:FilteredIndex
.
static class FilteredIndexExtension
{
public static IndexBuilder Filtered(this IndexBuilder indexBuilder, string condition)
{
indexBuilder.HasAnnotation("SqlServer:FilteredIndex", condition);
return indexBuilder;
}
}
Następnie należy zezwolić, aby ta adnotacja została faktycznie uwzględniona w ramach migracji. Musisz zastąpić domyślne zachowanie SqlServerMigrationsAnnotationProvider
dla budowniczych indeksu.
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);
}
}
Teraz jest najtrudniejsza część. Musimy zastąpić domyślne zachowanie SqlServerMigrationsSqlGenerator
dotyczące indeksów.
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);
}
}
}
Jak widać, wzywamy generator podstawy tutaj, więc nasz warunek zostanie dodany na końcu tego nie zmieniając go. Musimy pamiętać, aby nie kończyć podstawowej instrukcji SQL tutaj (ostatni argument przekazany do metody base.Generate
to false
). Jeśli nasza adnotacja jest ustawiona, możemy dodać jej wartość po klauzuli WHERE
na końcu instrukcji SQL. Następnie, w zależności od argumentu przekazanego do tej metody, możemy w końcu zakończyć instrukcję lub pozostawić ją taką jaka jest.
Dla wszystkich części do pracy musimy zastąpić stare usługi ich nowymi wersjami, przesuwając metodę OnConfiguring
naszego DbContext
.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<SqlServerMigrationsAnnotationProvider, ExtendedSqlServerMigrationsAnnotationProvider>();
optionsBuilder.ReplaceService<SqlServerMigrationsSqlGenerator, ExtendedSqlServerMigrationsSqlGenerator>();
}
Teraz możemy wykorzystać naszą metodę rozszerzenia tak:
builder.HasIndex(a => a.Identity).IsUnique().Filtered("[End] IS NULL");
będzie generować migrację tak:
migrationBuilder.CreateIndex(
name: "IX_Activities_Identity",
table: "Activities",
column: "Identity",
unique: true)
.Annotation("SqlServer:FilteredIndex", "[End] IS NULL");
i po nazywając Script-Migration
commad w opakowaniu konsoli Menedżer ujrzymy wynikowy kod SQL:
CREATE UNIQUE INDEX [IX_Activities_Identity] ON [Activities] ([Identity]) WHERE [End] IS NULL;
Ta metoda może być w rzeczywistości wykorzystana do włączenia dowolnego niestandardowego generatora SQL do interfejsu EF core Fluent. Co najmniej tak długo, jak EF API pozostaje bez zmian.
Interesujący artykuł: http://stackoverflow.com/questions/29922099/how-to-add-an-index-on-multiple-columns- with-asc-desc-sort-using-the-fluent-api – Elisabeth