type property primary many framework foreign enum define attribute c# entity-framework indexing entity-framework-6

c# - property - En Entity Framework 6.1(no Core), ¿cómo puedo usar el IndexAttribute para definir un índice agrupado?



entity framework enum type (5)

Entity Framework 6.1 (código primero) ha agregado la posibilidad de agregar índices a través de IndexAttribute . El atributo toma un parámetro para especificar si el índice debe ser agrupado o no agrupado.

Al mismo tiempo, AFAIK, Entity Framework requiere que cada entidad tenga una clave principal (anotada con el KeyAttribute ), y esa clave principal siempre se crea como una clave agrupada .

Por lo tanto, tan pronto como aplico el IndexAttribute con IsClustered = true , recibo un error porque, debido a la clave, ya hay un índice agrupado.

Entonces, ¿cómo puedo crear un índice agrupado que no sea la clave principal usando el IndexAttribute ? ¿Se puede IsClustered propiedad IsClustered del IndexAttribute ?

(Para un poco más de contexto: estoy mapeando una tabla que solo se usa para leer a través de las consultas LINQ. No necesito insertar, actualizar o eliminar entidades de esa tabla. Por lo tanto, no necesito una clave principal idealmente, me gustaría una tabla sin una clave principal, pero con un índice agrupado no único optimizado para la lectura.

Editar (2014-04-11): Consulte también https://entityframework.codeplex.com/workitem/2212 .


A continuación se muestra el código basado en la respuesta de Raditch que funcionó para mí. Esto permite que las claves primarias se agrupen por defecto. Es posible que deba modificarse, ya que no utilizamos las migraciones ef integradas para manejar los cambios.

public class NonClusteredPrimaryKeySqlMigrationSqlGenerator : SqlServerMigrationSqlGenerator { public override IEnumerable<System.Data.Entity.Migrations.Sql.MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken) { var primaries = migrationOperations.OfType<CreateTableOperation>().Where(x => x.PrimaryKey.IsClustered).Select(x => x.PrimaryKey).ToList(); var indexes = migrationOperations.OfType<CreateIndexOperation>().Where(x => x.IsClustered).ToList(); foreach (var index in indexes) { var primary = primaries.Where(x => x.Table == index.Table).SingleOrDefault(); if (primary != null) { primary.IsClustered = false; } } return base.Generate(migrationOperations, providerManifestToken); } } public class EFCustomConfiguration : DbConfiguration { public EFCustomConfiguration() { SetMigrationSqlGenerator("System.Data.SqlClient", () => new NonClusteredPrimaryKeySqlMigrationSqlGenerator()); } }


Diciéndole la verdad, el atributo de índice es totalmente redundante y no es adecuado para el desarrollo profesional. Carecen de funcionalidad central y se centran en cosas que tienen poco sentido.

¿Por qué? Porque nunca puede y debe ser tan flexible como un script de compilación. El índice agrupado es solo una cosa; lo próximo que me perdería es un índice filtrado, principalmente en la forma del "Índice único para un índice no nulo, no único para un nulo" en un campo, que uso muy regularmente para opcional códigos únicos (porque en SQL Server un NULL es igual a otro NULL en la generación de SQL, por lo que solo puede tener un NULL a la vez en un índice único).

Si fuera usted, me mantendría alejado de la generación de bases de datos (y de las migraciones) y utilizaría un enfoque clásico de configuración / migración de scripts. Eso es algo en lo que puedes hacer migraciones de pasos múltiples más complejas sin la posibilidad de una pérdida. EF no maneja nada más que los escenarios más básicos, y en estas áreas dudo que sea suficiente. Puede ser porque yo también, y en su mayoría, trabajo en grandes bases de datos donde hacemos nuestros cambios con mucho cuidado; agregar un índice puede llevar algo de tiempo cuando se alcanza un número de dos dígitos de miles de millones de filas (! 0+).

Preferiría que los desarrolladores se centraran en algunas de las áreas que faltan en las que no se puede trabajar mejor y más fácilmente, como el rendimiento, como las funciones básicas de ORM (mejores enumeraciones, almacenamiento en caché de segundo nivel, API de eliminación masiva, más inserciones y actualizaciones de rendimiento: son factibles). Código Primero es bueno. El primer código que genera y mantiene la base de datos es difícil en situaciones extremadamente simples.


Escribo aquí mi solución si alguien todavía está interesado en este tema. Debajo del código cambia la salida del comando add-migration.

public class CustomMigrationCodeGenerator : CSharpMigrationCodeGenerator { protected override void Generate(CreateTableOperation createTableOperation, IndentedTextWriter writer) { if (createTableOperation.Columns.Any(x => x.Name == "Index") && createTableOperation.Columns.Any(x => x.Name == "Id")) { if (createTableOperation.PrimaryKey != null) { createTableOperation.PrimaryKey.IsClustered = false; } } base.Generate(createTableOperation, writer); } }

Puede registrar este generador en la configuración de migración:

internal sealed class Configuration : DbMigrationsConfiguration<Ubrasoft.Freeman.WebApi.Db.MainDb> { public Configuration() { AutomaticMigrationsEnabled = false; CodeGenerator = new CustomMigrationCodeGenerator(); SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator()); } protected override void Seed(Ubrasoft.Freeman.WebApi.Db.MainDb context) { } }

Y aquí está el código de migración generado:

public override void Up() { CreateTable( "Tenant.Tenant", c => new { Id = c.Guid(nullable: false), TenantNo = c.Byte(nullable: false), Name = c.String(nullable: false, maxLength: 20), Index = c.Int(nullable: false, identity: true), CreatedDate = c.DateTime(nullable: false, precision: 0, storeType: "datetime2"), UpdatedDate = c.DateTime(nullable: false, precision: 0, storeType: "datetime2"), IsDeleted = c.Boolean(nullable: false), }) .PrimaryKey(t => t.Id, clustered: false) .Index(t => t.Index, unique: true, clustered: true); }

Here está el artículo acerca de MigrationCodeGenerator personalizado.


Puede derivar su propia clase de SqlServerMigrationSqlGenerator y cambiar la creación de pk allí:

public class NonClusteredPrimaryKeySqlMigrationSqlGenerator : SqlServerMigrationSqlGenerator { protected override void Generate(System.Data.Entity.Migrations.Model.AddPrimaryKeyOperation addPrimaryKeyOperation) { addPrimaryKeyOperation.IsClustered = false; base.Generate(addPrimaryKeyOperation); } protected override void Generate(System.Data.Entity.Migrations.Model.CreateTableOperation createTableOperation) { createTableOperation.PrimaryKey.IsClustered = false; base.Generate(createTableOperation); } protected override void Generate(System.Data.Entity.Migrations.Model.MoveTableOperation moveTableOperation) { moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false; base.Generate(moveTableOperation); }

Ejemplo completo aquí https://entityframework.codeplex.com/workitem/2163


Solo puede haber un índice agrupado en una tabla y, de forma predeterminada, Entity Framework / Sql Server lo coloca en la clave principal.

Entonces, ¿de qué sirve el atributo IsClustered en un índice que no es la clave principal? ¡Buena pregunta! (+1)

Esta clase:

public class Blog { [Key()] public int Id { get; set; } [MaxLength(256)]//Need to limit size of column for clustered indexes public string Title { get; set; } [Index("IdAndRating", IsClustered = true)] public int Rating { get; set; } }

generará esta migración:

public override void Up() { CreateTable( "dbo.Blogs", c => new { Id = c.Int(nullable: false, identity: true), Title = c.String(maxLength: 256), Rating = c.Int(nullable: false), }); .PrimaryKey(t => t.Id) .Index(t => t.Rating, clustered: true, name: "IdAndRating"); }

Alterar la migración a esto:

public override void Up() { CreateTable( "dbo.Blogs", c => new { Id = c.Int(nullable: false, identity: true), Title = c.String(maxLength: 256), Rating = c.Int(nullable: false), }); CreateIndex("dbo.Blogs", new[] { "Rating", "Title" }, clustered: true, name: "IdAndRating"); }

Y eso debería crear su tabla sin una clave principal pero con el índice agrupado en las otras columnas

EDITAR En su escenario en el que no necesita insertar, actualizar o eliminar datos, no necesita una entidad completa, puede usar consultas de sql raw para completar las clases. Necesitará agregar su propio sql a la migración para crear la tabla porque EF no lo automatizará, pero eso significa que puede crear la tabla y el índice tal como lo desea.