Entity Framework生成的表,默认主键作为聚集索引。如果主键是Guid,就会带来性能问题。原因可自行查找相关资料,本文主要介绍一下如何使用Entity Framework创建非聚集索引的主键。要注意的是,本文使用的是EF6。

首先准备工作,使用下面的模型:

    public class Product
    {
        public Guid Id { get; set; }

        public string Name { get; set; }
    }

我们还是先来看一下EF的默认生成。

EF的默认生成

按照下面的步骤生成数据库:

  1. 执行Enable-Migrations命令启动EF迁移
  2. 执行Add-Migration Init 搭建迁移“Init",如下:
            public partial class Init : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "dbo.Products",
                    c => new
                        {
                            Id = c.Guid(nullable: false),
                            Name = c.String(nullable: false),
                        })
                    .PrimaryKey(t => t.Id);
                
            }
            
            public override void Down()
            {
                DropTable("dbo.Products");
            }
        }
    
  3. 执行Update-Database生成数据库

通过查看索引,可以看到Guid的主键Id是聚集索引:

Entity Framework 6创建非聚集索引主键-程序旅途

下面看看如何生成非聚集索引主键

EF生成非聚集索引主键

这里我们首先要考虑的是在哪个列上加聚集索引,因为上面的例子中只有2列,下面再添加一个自增列,然后在这个自增列上添加聚集索引。按照下面的步骤操作:

  1. 在Product实体中,添加如下AutoId属性:
            public int AutoId { get; set; }
    
  2. 配置AutoId为自增长:
                Property(t => t.AutoId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    
  3. 执行Add-Migration AddAutoId 搭建迁移“AddAutoId”
        public partial class AddAutoId : DbMigration
        {
            public override void Up()
            {
                AddColumn("dbo.Products", "AutoId", c => c.Int(nullable: false, identity: true));
            }
            
            public override void Down()
            {
                DropColumn("dbo.Products", "AutoId");
            }
        }
    
  4. 修改迁移“AddAutoId",在Up方法中AddColumn下面添加如下代码:
                //移除主键约束
                DropPrimaryKey("dbo.Products");
                //移除主键索引
                DropIndex("dbo.Products", "PK_dbo.Products");
                //在AutoId列上创建聚集索引
                CreateIndex("dbo.Products", "AutoId", unique: true, clustered: true);
                //添加主键约束
                AddPrimaryKey("dbo.Products", "Id", clustered: false);
    

    在Down方法中DropColumn上面添加如下代码:

                //移除主键约束
                DropPrimaryKey("dbo.Products");
                //移除聚集索引IX_AutoId
                DropIndex("dbo.Products", "IX_AutoId");
                //在主键Id上创建主键聚集索引
                AddPrimaryKey("dbo.Products", "Id", clustered: true);
    
  5. 执行Update-Database更新到数据库

再来看一下索引情况:

Entity Framework 6创建非聚集索引主键-程序旅途

是不是正是我们期望的结果呢?

通过上面的例子可以看到,在添加聚集索引之前,首先要移除主键约束,如果还存在外键约束,那么还要先移除外键约束。下面我们再来看一个带有外键约束的例子。按照下面的步骤操作:

  1. 执行Update-Database -TargetMigration Init命令,回迁到初始状态
  2. 将Product实体中的AutoId属性和它的映射配置注释掉
  3. 删除已经生成的AddAutoId迁移
  4. 新添加一个实体ReferenceProduct,它引用Product:
        public class ReferenceProduct
        {
            public Guid Id { get; set; }
    
            public Guid ProductId { get; set; }
            public Product Product { get; set; }
        }
    
  5. 添加它的配置映射:
        public class ReferenceProductConfiguration : EntityTypeConfiguration
        {
            public ReferenceProductConfiguration()
            {
                HasKey(t => t.Id);
                HasRequired(t => t.Product).WithMany().HasForeignKey(t => t.ProductId);
            }
        }
    
  6. 执行Add-Migration AddNewEntity,添加迁移"AddNewEntity"
  7. 执行Update-Database更新到数据库
  8. 取消第2步注释的部分
  9. 执行Add-Migration AddAutoId,生成迁移AddAutoId(这一步跟上面生成的AddAutoId代码是一样的)
  10. 修改修改迁移“AddAutoId",这一步跟上面差不多,只不过先要移除外键约束。在Up方法中AddColumn下面添加如下代码:
                //移除外键约束
                DropForeignKey("dbo.ReferenceProducts", "FK_dbo.ReferenceProducts_dbo.Products_ProductId");
    
                DropPrimaryKey("dbo.Products");
    
                DropIndex("dbo.Products", "PK_dbo.Products");
    
                CreateIndex("dbo.Products", "AutoId", unique: true, clustered: true);
    
                AddPrimaryKey("dbo.Products", "Id", clustered: false);
                //添加外键约束
                AddForeignKey("dbo.ReferenceProducts", new string[] { "ProductId" }, "dbo.Products");
    

    在Down方法中DropColumn上面添加如下代码:

                //移除外键约束
                DropForeignKey("dbo.ReferenceProducts", "FK_dbo.ReferenceProducts_dbo.Products_ProductId");
    
                DropPrimaryKey("dbo.Products");
    
                DropIndex("dbo.Products", "IX_AutoId");
    
                AddPrimaryKey("dbo.Products", "Id", clustered: true);
    
                //添加外键约束
                AddForeignKey("dbo.ReferenceProducts", new string[] { "ProductId" }, "dbo.Products");
    
  11. 执行Update-Database更新到数据库

上面说了2种情况,带有主键约束和外键约束的,还有一种可能就是我们一开始创建实体的时候就添加了需要加聚集索引的列(也就是上文的AutoId),这种情况下按照下面的步骤操作:

  1. 我们添加一个新的实体Book,来演示这种情况:
        public class Book
        {
            public Guid Id { get; set; }
    
            public int AutoId { get; set; }
    
            public string Name { get; set; }
        }
    
  2. 添加映射配置BookConfiguration
  3. 执行Add-Migration AddBook,搭建迁移“AddBook”:
            public override void Up()
            {
                CreateTable(
                    "dbo.Books",
                    c => new
                        {
                            Id = c.Guid(nullable: false),
                            AutoId = c.Int(nullable: false, identity: true),
                            Name = c.String(nullable: false),
                        })
                    .PrimaryKey(t => t.Id);        
            }
            
            public override void Down()
            {
                DropTable("dbo.Books");
            }
    
  4. 修改Up方法中的PrimaryKey并添加Index:
                CreateTable(
                    "dbo.Books",
                    c => new
                        {
                            Id = c.Guid(nullable: false),
                            AutoId = c.Int(nullable: false, identity: true),
                            Name = c.String(nullable: false),
                        })
                        .PrimaryKey(t => t.Id, clustered: false)
                        .Index(t => t.AutoId, unique: true, clustered: true);
    
  5. 执行Update-Database更新到数据库

总结

上面提到的3种情况,前两种适合表已经存在的情况,这种情况下需要先移除主键约束或外键约束,第三种情况适合新创建表的情况。

有关Entity Framework迁移的内容,请参考我的其他博文:Entity Framework 迁移