2015-10-01 11 views
19

Używam EF 6.1.x Code First.Entity Framework Filter Index

Przeczytałem, że indeks z wyrażeniem filtru nie jest obsługiwany przez najnowszą wersję EF.

Nie ma również rozwiązanie na SO:

EF 6.1 Unique Nullable Index

rok później, co jest sposobem pracy, aby pracę Index filtra z pierwszego kodeksu i DbMigrations?

CREATE UNIQUE NONCLUSTERED INDEX [IX_DefaultLanguageApplicationId] ON [dbo].[Languages] 
(
    [IsDefaultLanguage] ASC, 
    [ApplicationId] ASC, 
) 
WHERE ([IsDefaultLanguage]=(1)) 
+0

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

Odpowiedz

20

W EF 6.1, sposób pracy, aby tę pracę z kod najpierw i DbMigrations jest użycie metody Sql w klasie DbMigration:

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"); 
    } 
} 

Ale zdaję sobie sprawę, że jesteś prawdopodobnie zadając jeśli możesz create an index using the IndexAttribute introduced in 6.1, ale z filtrem - odpowiedź na to pytanie brzmi „Nie”

Prawie duplikat: Entity Framework 6.1 - Create index with INCLUDE statement

+0

Tak, szukam http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/ ALE faktycznie wiedziałem, że nie jest to jeszcze możliwe. Wciąż dziękuję! – Elisabeth

+0

Dodałem tę klasę częściową i zrobiłem "aktualizującą bazę danych", a następnie wszystkie migracje zostały jawnie zastosowane, ALE ten nowy indeks nie został utworzony w bazie danych ??? – Elisabeth

+0

Ok Najpierw utworzyłem pustego migratina, ALE dostaję ten błąd: Numer błędu: 102, Stan: 1, Klasa: 15 Niepoprawna składnia w pobliżu ")". Czy mógłbyś poprawić błąd ")"? – Elisabeth

1

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.