在Entity Framework 6.1(非Core)中,如何使用IndexAttribute定义聚簇索引?

entity framework6.1(代码优先)增加了通过IndexAttribute添加索引的可能性。 该属性接受一个参数,用于指定索引应该是群集还是非群集。

同时,AFAIK,Entity Framework要求每个实体都有一个主键(使用KeyAttribute注释),并且该主键始终作为聚簇键创建。

因此,只要我使用IsClustered = true应用IndexAttribute ,就会出现错误,因为由于密钥,已经存在聚簇索引。

那么,如何使用IndexAttribute创建不是主键的聚簇索引? IsClustered属性IndexAttribute可用?

(有关更多上下文:我正在映射一个仅用于通过LINQ查询进行读取的表。我不需要实际插入,更新或删除该表中的实体。因此,我不需要主键理想情况下,我想要一个没有主键的表,但是有一个非唯一的聚簇索引,专为阅读而优化。)

编辑(2014-04-11):另请参阅https://entityframework.codeplex.com/workitem/2212 。

表上只能有一个聚簇索引,默认情况下,Entity Framework / Sql Server将其放在主键上。

那么,对于不是主键的索引, IsClustered属性有什么用? 好问题! (1)

这个class:

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

将生成此迁移:

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

改变迁移到此:

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

这应该创建没有主键但在其他列上使用聚簇索引的表

编辑在您不需要插入,更新或删除数据的场景中,您不需要一个完整的实体,您可以使用原始SQL查询来填充类。 您需要将自己的sql添加到迁移中以创建表,因为EF不会自动化它,但这意味着您可以根据需要创建表和索引。

您可以从SqlServerMigrationSqlGenerator派生自己的类并在那里更改pk创建:

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

完整示例https://entityframework.codeplex.com/workitem/2163

告诉你真相 – IndexAttribute是完全多余的,不适合专业发展。 他们缺乏核心function,专注于没有意义的东西。

为什么? 因为它永远不会,并且应该像构建脚本一样灵活。 聚簇索引只是一件事 – 接下来我会想念的是一个过滤索引,主要是在字段上使用“非空的唯一索引,非唯一索引为空”,我碰巧经常使用它来选择唯一代码(因为在SQL Server中,NULL等于SQL生成中的另一个NULL,因此在唯一索引中一次只能有一个NULL)。

如果我是你,我将远离数据库生成 – 和迁移 – 并使用经典的设置/迁移脚本方法。 这是你可以进行更复杂的多步骤迁移而不会丢失的东西。 除了最基本的场景外,EF不会处理任何事情 – 在这些方面我怀疑这已经足够了。 可能是因为我也主要在大型数据库上工作,我们非常谨慎地进行更改 – 添加索引可能需要一些时间才能达到数十亿行(!0+)的两位数。

我希望开发人员能够专注于一些无法轻松更好地解决的缺失领域,比如性能,如核心ORMfunction(更好的枚举,二级缓存,批量删除API,更多性能插入和更新 – 所有这些是可行的)。 Code First很不错。 Code First生成和维护数据库是非常简单的场景之外的痛苦。

以下是基于raditch的答案的代码,对我有用。 这允许主键默认为群集。 它可能需要调整,因为我们不使用内置的ef迁移来实际处理更改

 public class NonClusteredPrimaryKeySqlMigrationSqlGenerator : SqlServerMigrationSqlGenerator { public override IEnumerable Generate(IEnumerable migrationOperations, string providerManifestToken) { var primaries = migrationOperations.OfType().Where(x => x.PrimaryKey.IsClustered).Select(x => x.PrimaryKey).ToList(); var indexes = migrationOperations.OfType().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()); } } 

如果有人对此主题感兴趣,我会在此写下我的解决方案。 下面的代码更改了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); } } 

您可以在迁移配置中注册此生成器:

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

这是生成的迁移代码:

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

以下是有关自定义MigrationCodeGenerator的文章。