实体框架代码第一,聚集索引&复合键

时间:2016-05-24 15:10:29

标签: c# .net entity-framework ef-code-first data-annotations

我正在尝试使用EF的Code First方法在表中的复合键所使用的列之一上添加非唯一的聚簇索引。到目前为止,我有以下型号。请注意我要添加的IX_PackageTargetPackageId属性上的PackageTarget.PackageId索引属性:

public class Package
{
    [Key]
    public int Id { get; set; }
    [Required]
    [Index("IX_PackageName", IsUnique = true)]
    public string Name { get; set; }
}

public class Target
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Value { get; set; }
}

public class PackageTarget
{
    [Key]
    [Column(Order = 0)]
    [Index("IX_PackageTargetPackageId", IsClustered = true)]
    public int PackageId { get; set; }

    [Key]
    [Column(Order = 1)]
    public int TargetId { get; set; }

    [ForeignKey("PackageId")]
    public virtual Package Package { get; set; }

    [ForeignKey("TargetId")]
    public virtual Target Target { get; set; }
}

我有以下为我自动生成的迁移:

CreateTable(
    "dbo.PackageTargets",
    c => new
        {
            PackageId = c.Int(nullable: false),
            TargetId = c.Int(nullable: false),
        })
    .PrimaryKey(t => new { t.PackageId, t.TargetId })
    .ForeignKey("dbo.Packages", t => t.PackageId, cascadeDelete: true)
    .ForeignKey("dbo.Targets", t => t.TargetId, cascadeDelete: true)
    .Index(t => t.PackageId, clustered: true, name: "IX_PackageTargetPackageId")
    .Index(t => t.TargetId);

CreateTable(
    "dbo.Packages",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            Name = c.String(nullable: false)
        })
    .PrimaryKey(t => t.Id)
    .Index(t => t.Name, unique: true, name: "IX_PackageName");

CreateTable(
    "dbo.Targets",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            Value = c.String(nullable: false)
        })
    .PrimaryKey(t => t.Id);

我对迁移的唯一修改是将clustered: false添加到PackageTargets主键,因为我希望将IX_PackageTargetPackageId索引转换为群集。

在上述修订之后,我仍无法运行迁移,因为发生以下异常:

Cannot create more than one clustered index on table 'dbo.PackageTargets'. 
Drop the existing clustered index 'PK_dbo.PackageTargets' before creating another.

从我的迁移中,我看不到其他聚簇索引。我错过了什么吗?

2 个答案:

答案 0 :(得分:0)

你说:

  

尝试添加非唯一的聚簇索引

在PackageID属性上有这个属性:

[Index("IX_PackageTargetPackageId", IsClustered = true)]

但是您还在多个列上定义了[Key]属性(因为EF需要主键),从而创建复合主键。在EF代码中,首先不可能(至少没有跳过一些箍)来定义非聚集主键,因此复合主键也总是成为聚类键。

您不能在表格上拥有两个聚簇索引(请参阅此SO文章:What do Clustered and Non clustered index actually mean?

聚簇索引根据密钥对磁盘上的行进行物理重新排序,而非群集索引则不进行物理重新排序。由于无法同时以两种不同的方式对行进行物理排序,因此您无法指定两个聚簇索引。

除非您已完成性能测试并看到手动指定群集密钥的真正好处,否则我建议您只将IsClustered设置为false并允许EF管理群集密钥。

答案 1 :(得分:0)

正如DVK所说。在您的情况下,EF尝试制作两个不同的聚簇索引。首先聚类为PK,第二个聚类为索引属性中的descibed。你必须做不集群PK。  不幸的是,在EF 6的最新稳定版本中,它不可能或不明显。当然你总是可以使用纯SQL的DbMigration的Sql()方法。但这种解决方案并不优雅。 EF 6.2的斗鱼版有这种可能性。

Install-Package EntityFramework -Version 6.2.0-beta1

然后通过流畅的api你必须定义PK和你的聚集索引。这个例子来自我的项目。

 modelBuilder.Entity<OrderWaybill>()
    .HasKey(o => new { o.GUID, o.OrderDataCode }
            , config => config.IsClustered(false));
 modelBuilder.Entity<OrderWaybill>()
    .HasIndex("IX_PRIMARY_SELECT", IndexOptions.Clustered
                , ri=> ri.Property(x=>x.OrderDataCode)
                , ri => ri.Property(x => x.Number));