tutorial mvc framework first existing español code entity-framework entity-framework-6 ef-code-first

mvc - Índice de filtro de Entity Framework



entity framework database first español (3)

Yo uso el Código EF 6.1.x Primero.

He leído que un índice con expresión de filtro no es compatible con la última versión de EF.

Tampoco hay solución en SO:

Índice de nullable único de EF 6.1

Un año más tarde, ¿cuál es la manera de trabajar para que un Índice de filtros funcione con Code First y DbMigrations?

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


En EF 6.1, la forma de trabajar para hacer que funcione con Code First y DbMigrations es usar el método Sql en la clase 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"); } }

Pero me doy cuenta de que probablemente esté preguntando si puede crear un índice utilizando el atributo de índice introducido en 6.1 , pero con un filtro, la respuesta es "No".

Casi un duplicado de: Entity Framework 6.1 - Crear índice con la declaración INCLUDE


Sé que la publicación original se refería a la versión 6.1 de EF, pero después de algunas investigaciones he encontrado una manera de agregar un método de extensión para índices filtrados a la api fluida de EF Core (versión 1.1) . Quizás a alguien le resulte útil (y tal vez haya una forma de implementarlo también en versiones anteriores). Aunque tengo que advertirte. Como esta solución usa clases dentro de Microsoft.EntityFrameworkCore.Migrations.Internal espacios de nombres Microsoft.EntityFrameworkCore.Migrations.Internal y Microsoft.EntityFrameworkCore.Infrastructure , no se garantiza que este código funcionará después de que se actualice EF. Hay un masaje incluido en un resumen de cada clase dentro de estos espacios de nombres que dice que

Esta API puede cambiar o ser eliminada en futuras versiones

, por lo que ha sido advertido.

Pero al punto.

Primero tienes que crear un método de extensión estándar para el IndexBuilder . Su principal responsabilidad será agregar una nueva anotación con la condición al índice construido. Uno usará este método luego con la api fluida. Para que no llame a nuestra anotación SqlServer:FilteredIndex .

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

A continuación, debe permitir que esta anotación se incluya realmente dentro de las migraciones. Debe anular el comportamiento predeterminado de SqlServerMigrationsAnnotationProvider para los creadores de índices.

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

Ahora viene la parte más difícil. Tenemos que anular el comportamiento predeterminado de SqlServerMigrationsSqlGenerator respecto a los índices.

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

Como puede ver, estamos llamando al generador base aquí, por lo que nuestra condición se agregará al final sin alterarlo. Debemos recordar no terminar la instrucción SQL base aquí (el último argumento pasado al método base.Generate es false ). Si nuestra anotación está establecida, podemos agregar su valor después de la cláusula WHERE al final de la declaración SQL. Después de eso, dependiendo del argumento pasado a este método, finalmente podemos terminar la declaración o dejarla como está.

Para que todas esas partes funcionen, debemos reemplazar los servicios antiguos con sus nuevas versiones anulando el método OnConfiguring de nuestro DbContext .

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

Ahora podemos usar nuestro método de extensión así:

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

Se generará una migración como esta:

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

Y después de llamar Script-Migration comando de Script-Migration script en la Consola del administrador de paquetes veremos un SQL resultante como esto:

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

Este método se puede usar para incluir cualquier generador de SQL personalizado en la api con fluidez de ef core. Al menos mientras la API EF permanezca igual.


Tenga en cuenta que ahora EF Core 2.1.X agregó soporte integrado para los índices filtrados a través de la extensión HasFilter en el IndexBuilder , por lo que ya no se requiere una implementación personalizada.

Ver this para más detalles.