2015-10-01 11 views
19

Ich benutze EF 6.1.x Code zuerst.Entity Framework Filter Index

Ich habe gelesen, dass ein Index mit Filterausdruck von EF zuletzt nicht unterstützt wird.

Es gibt auch keine Lösung auf SO:

EF 6.1 Unique Nullable Index

Ein Jahr später, was die Arbeits Art und Weise einen Filter Index Arbeit mit Code ersten DbMigrations zu machen?

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

Interessanter Artikel: http://stackoverflow.com/questions/29922099/how-to-add-an-index-on-multiple-columns-with-asc-des-sort-using-the-fluent-api – Elisabeth

Antwort

20

In EF 6.1, die Arbeits Art und Weise, die diese Arbeit mit-Code ersten DbMigrations zu machen, ist die Sql Methode in der DbMigration Klasse zu verwenden:

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

Aber ich merke, dass Sie wahrscheinlich fragen wenn Sie können create an index using the IndexAttribute introduced in 6.1, aber mit einem Filter - die Antwort auf diese Frage „Nein“

Fast ein Duplikat: Entity Framework 6.1 - Create index with INCLUDE statement

+0

Ja, ich suche nach http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/ ABER ich wusste, dass es tatsächlich noch nicht möglich ist. Immer noch Danke! – Elisabeth

+0

Ich habe diese partielle Klasse hinzugefügt und "update-database" durchgeführt, dann wurden alle Migrationen explizit angewendet, aber dieser neue Index wurde nicht in der Datenbank erstellt ??? – Elisabeth

+0

Ok Ich habe zuerst eine leere Migratin erstellt, aber dann bekomme ich diesen Fehler: Fehlernummer: 102, Status: 1, Klasse: 15 Falsche Syntax in der Nähe von ')'. Würdest du bitte den ")" Fehler korrigieren? – Elisabeth

1

Ich weiß, dass der ursprüngliche Beitrag auf die Version 6.1 von EF verwiesen, aber nach einigen Nachforschungen habe ich einen Weg gefunden, eine Erweiterungs-Methode für gefilterte Indizes zu der fließenden API von EF Core (1.1 Version) hinzuzufügen. Vielleicht wird jemand das nützlich finden (und vielleicht gibt es eine Möglichkeit, dies auch in älteren Versionen zu implementieren). Ich muss dich aber warnen. Da diese Lösung Klassen innerhalb von Microsoft.EntityFrameworkCore.Migrations.Internal und Microsoft.EntityFrameworkCore.Infrastructure Namespaces verwendet, ist es nicht garantiert, dass dieser Code nach der Aktualisierung von EF funktioniert. Es ist eine Massage in einer Zusammenfassung jeder Klasse in diesen Namespaces enthalten, die besagt, dass

This API may change or be removed in future releases

, so dass Sie gewarnt worden sind.

Aber auf den Punkt.

Zuerst müssen Sie eine Standard-Erweiterungsmethode für die IndexBuilder erstellen. Seine Hauptaufgabe besteht darin, dem konstruierten Index eine neue Annotation mit der Bedingung hinzuzufügen. Man wird diese Methode später mit der fließenden API anwenden. Lass uns unsere Annotation anrufen SqlServer:FilteredIndex.

static class FilteredIndexExtension 
{ 
    public static IndexBuilder Filtered(this IndexBuilder indexBuilder, string condition) 
    { 
     indexBuilder.HasAnnotation("SqlServer:FilteredIndex", condition); 

     return indexBuilder; 
    } 
} 

Als Nächstes müssen Sie zulassen, dass diese Anmerkung tatsächlich in Migrationen enthalten ist. Sie müssen das Standardverhalten von SqlServerMigrationsAnnotationProvider für Indexersteller überschreiben.

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

Jetzt kommt der schwierigste Teil. Wir müssen das Standardverhalten von SqlServerMigrationsSqlGenerator in Bezug auf Indizes überschreiben.

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

Wie Sie sehen können, rufen wir den Basis-Generator hier, so dass unser Zustand, ohne sie zu ändern am Ende hinzugefügt werden. Wir müssen daran denken, die Basis-SQL-Anweisung hier nicht zu beenden (das letzte an die base.Generate-Methode übergebene Argument ist false). Wenn unsere Annotation gesetzt ist, können wir ihren Wert nach der WHERE-Klausel am Ende der SQL-Anweisung anhängen. Danach können wir abhängig von dem an diese Methode übergebenen Argument die Anweisung schließlich beenden oder sie so belassen wie sie ist.

Damit all diese Teile funktionieren, müssen wir alte Dienste durch ihre neuen Versionen ersetzen, indem wir die OnConfiguring Methode unserer DbContext überschreiben.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
    { 
     optionsBuilder.ReplaceService<SqlServerMigrationsAnnotationProvider, ExtendedSqlServerMigrationsAnnotationProvider>(); 
     optionsBuilder.ReplaceService<SqlServerMigrationsSqlGenerator, ExtendedSqlServerMigrationsSqlGenerator>(); 
    } 

Jetzt können wir unsere Extension-Methode wie folgt verwenden:

builder.HasIndex(a => a.Identity).IsUnique().Filtered("[End] IS NULL"); 

Es wird Migration wie folgt erzeugen:

migrationBuilder.CreateIndex(
      name: "IX_Activities_Identity", 
      table: "Activities", 
      column: "Identity", 
      unique: true) 
      .Annotation("SqlServer:FilteredIndex", "[End] IS NULL"); 

Und nach Script-Migration commad in Package Manager-Konsole rufen wir werden sehen, ein resultierendes SQL wie folgt:

CREATE UNIQUE INDEX [IX_Activities_Identity] ON [Activities] ([Identity]) WHERE [End] IS NULL; 

Diese Methode kann tatsächlich verwendet werden, um einen benutzerdefinierten SQL-Generator in ef core fluent api einzubeziehen. Zumindest solange die EF API gleich bleibt.

Verwandte Themen