Entity Framework Code First、聚簇索引和复合键
Entity Framework Code First, Clustered Indexes & Composite Keys
我正在尝试使用 EF 的 Code First 方法在 table 中的复合键使用的列之一上添加非唯一的聚集索引。到目前为止,我有以下模型。请注意 PackageTarget.PackageId
属性 上的 IX_PackageTargetPackageId
索引属性 我正在尝试添加:
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.
从我的迁移中,我看不到其他聚簇索引存在。我错过了什么吗?
你说:
attempting to add a non-unique, clustered index
并且在 PackageID 上有这个属性 属性:
[Index("IX_PackageTargetPackageId", IsClustered = true)]
但是您还在多个列上定义了 [Key]
属性(因为 EF 需要一个主键),从而创建了一个复合主键。在 EF 代码中,不可能(至少不跳过一些环节)定义非集群主键,因此您的复合主键也始终是集群键。
您不能在 table 上有 两个 聚簇索引(请参阅这篇 SO 文章:What do Clustered and Non clustered index actually mean?)
聚集索引根据键对磁盘上的行进行物理重新排序,而非聚集索引不会进行物理重新排序。由于不可能同时以两种不同的方式对行进行物理排序,因此您不能指定两个聚簇索引。
除非您已完成性能测试并看到手动指定集群密钥的真正好处,否则我建议仅将 IsClustered
设置为 false 并允许 EF 管理集群密钥。
正如 DVK 所说。在您的情况下,EF 试图创建两个不同的聚集索引。首先聚类用于 PK,其次用于在索引属性中描述。你必须做出不集群的PK。
不幸的是,在 EF 6 的最新稳定版本中,它不可能或不明显。当然,您始终可以将 DbMigration 的 Sql() 方法与纯 SQL 一起使用。但是这个解决方案看起来并不优雅。
斗鱼版EF 6.2就有这种可能。
Install-Package EntityFramework -Version 6.2.0-beta1
然后通过 fluent 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));
我正在尝试使用 EF 的 Code First 方法在 table 中的复合键使用的列之一上添加非唯一的聚集索引。到目前为止,我有以下模型。请注意 PackageTarget.PackageId
属性 上的 IX_PackageTargetPackageId
索引属性 我正在尝试添加:
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.
从我的迁移中,我看不到其他聚簇索引存在。我错过了什么吗?
你说:
attempting to add a non-unique, clustered index
并且在 PackageID 上有这个属性 属性:
[Index("IX_PackageTargetPackageId", IsClustered = true)]
但是您还在多个列上定义了 [Key]
属性(因为 EF 需要一个主键),从而创建了一个复合主键。在 EF 代码中,不可能(至少不跳过一些环节)定义非集群主键,因此您的复合主键也始终是集群键。
您不能在 table 上有 两个 聚簇索引(请参阅这篇 SO 文章:What do Clustered and Non clustered index actually mean?)
聚集索引根据键对磁盘上的行进行物理重新排序,而非聚集索引不会进行物理重新排序。由于不可能同时以两种不同的方式对行进行物理排序,因此您不能指定两个聚簇索引。
除非您已完成性能测试并看到手动指定集群密钥的真正好处,否则我建议仅将 IsClustered
设置为 false 并允许 EF 管理集群密钥。
正如 DVK 所说。在您的情况下,EF 试图创建两个不同的聚集索引。首先聚类用于 PK,其次用于在索引属性中描述。你必须做出不集群的PK。 不幸的是,在 EF 6 的最新稳定版本中,它不可能或不明显。当然,您始终可以将 DbMigration 的 Sql() 方法与纯 SQL 一起使用。但是这个解决方案看起来并不优雅。 斗鱼版EF 6.2就有这种可能。
Install-Package EntityFramework -Version 6.2.0-beta1
然后通过 fluent 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));