EF 6 代码优先为许多 table 生成额外的 table?
EF 6 code-first generating extra table for many-many table?
引用自 @Ogglas answer of this post,
请问EF再生成一个table是否正常?
如果附加的 table 不应该存在,那我做错了什么?请赐教。 TIA!
示例代码:
public class Aggregate
{
public Aggregate()
{
Episodes = new HashSet<Episode>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
...
public virtual ICollection<Episode> Episodes { get; set; }
}
public class Episode
{
public Episode()
{
Aggregates = new HashSet<Aggregate>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
...
public virtual ICollection<Aggregate> Aggregates { get; set; }
}
public class EpisodeAggregate
{
[Key]
[Column(Order = 1)]
[ForeignKey("Episode")]
public Guid EpisodeId { get; set; }
[Key]
[Column(Order = 2)]
[ForeignKey("Aggregate")]
public Guid AggregateId { get; set; }
public virtual Episode Episode { get; set; }
public virtual Aggregate Aggregate { get; set; }
public DateTime Timestamp { get; set; }
}
在我的 DbContext.cs:
public DbSet<EpisodeAggregate> EpisodeAggregates { get; set; }
你是对的。在关系数据库中,使用联结 table.
解决多对多关系
对于标准的多对多关系,你不需要提到这个连接点table; entity framework 通过在多对多关系的两边使用 virtual ICollection<...>
来识别多对多,并会自动为您创建 table。
为了测试我的数据库理论和 entity framework,我经常使用包含学校、学生和教师的简单数据库。学校-学生和学校-教师一对多,教师-学生多对多。我总是看到 Teacher-Student junction table 是自动创建的,无需提及它。
不过!
您的连接 table 不标准。标准联结 table 只有两列:EpisodeId
和 AggregateId
。它甚至没有额外的主键。组合 [EpisodeId, AggregateId] 已经是唯一的,可以用作主键。
您在 table EpisodeAggregate
中有一个额外的列:TimeStamp
。显然你想知道 Episode 和 Aggregate 何时相关。
“给我 Episode[4] 与 Aggregate[7] 关联时的时间戳”
这使得这个 table 不是标准的交汇点 table。 Episodes 和 Aggregates 之间没有多对多关系。您在 Episode 及其与聚合的关系之间建立了一对多关系,同样在聚合及其与 Episodes 的关系之间建立了一对多关系。
这使得您必须将多对多更改为一对多:
class Episode
{
public Guid Id { get; set; }
// every Episode has zero or more Relations with Aggregates (one-to-many)
public virtual ICollection<EpisodeAggregateRelation> EpisodeAggregateRelations { get; set; }
...
}
class Aggregate
{
public Guid Id { get; set; }
// every Episode has zero or more Relations with Episodes(one-to-many)
public virtual ICollection<EpisodeAggregateRelation> EpisodeAggregateRelations { get; set; }
...
}
class EpisodeAggregateRelation
{
// Every Relation is the Relation between one Episode and one Aggregate
// using foreign key:
public Guid EpisodeId { get; set; }
public Guid AggregateId { get; set; }
public virtual Episode Episode { get; set; }
public virtual Aggregate Aggregate { get; set; }
public DateTime Timestamp { get; set; }
}
如果您确定 Episode 和 Aggregate 之间始终最多只有一种关系,则可以使用组合 [EpisodeId, AggregateId] 作为主键。如果你认为这两个可能有几个关系,你需要添加一个单独的主键。
我经常在不同的数据库中使用我的类,因此我不喜欢属性,我在OnModelCreating中用流利的API解决它:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Episode>()
.HasKey(episode => episode.Id)
// define the one-to-many with EpisodeAggregateRelations:
.HasMany(episode => episode.EpisodeAggregateRelations)
.WithRequired(relation => relation.Episode)
.HasForeignKey(relation => relation.EpisodeId);
modelBuilder.Entity<Aggregate>()
.HasKey(aggregate => aggregate.Id)
// define the one-to-many with EpisodeAggregateRelations:
.HasMany(aggregate => aggregate .EpisodeAggregateRelations)
.WithRequired(relation => relation.Aggregate)
.HasForeignKey(relation => relation.aggregateId);
以上不需要!
因为你遵循了entity framework code first conventions,你可以省略这两个语句。 Entity framework 将识别主键和一对多关系。仅当您想要偏离约定时,例如非标准 table 名称,或者如果您想要定义列顺序:
modelBuilder.Entity<Episode>()
.ToTable("MySpecialTableName")
.Property(episode => episode.Date)
.HasColumnName("FirstBroadcastDate")
.HasColumnOrder(3)
.HasColumnType("datetime2");
但再次声明:您遵循了约定,不需要所有那些属性,如 Key、ForeignKey、DatabaseGenerated。和列顺序:谁在乎?让您的数据库管理系统决定最佳的列顺序。
我的建议是:尝试进行实验:忽略这种流利的方式 API 并检查您的单元测试是否仍然通过。五分钟后检查。
EpisodeAggregateRelation
有一些不标准的东西:它有一个复合主键。因此你需要定义这个。参见 Configuring a composite primary key
modelBuilder.Entity<EpisodeAggregateRelation>()
.HasKey(relation => new
{
relation.EpisodId,
relation.AggregateId
});
如果您已经在 Episodes 和 Aggregates 中定义了一对多关系,或者如果由于约定不需要它,则不必在这里再次提及这种关系。
如果需要,您可以将一对多放在 EpisodeAggregateRelation 的流畅 API 部分,而不是 Episode / Aggregate 的流畅 API 部分:
// every EpisodeAggregateRelation has one Episode, using foreign key
modelBuilder.Entity<EpisodeAggregateRelation>()
.HasRequired(relation => relation.Episode(
.WithMany(episode => episode.EpisodeAggregateRelations)
.HasForeignKey(relation => relation.EpisodeId);
// similar for Aggregate
最后一个提示
不要在构造函数中创建 HashSet。如果您获取数据,这是一种处理能力的浪费:您创建了 HashSet,它立即被 entity framework 创建的 ICollection<...>
所取代。
如果您不相信我:请尝试一下,看看您的单元测试是否通过,检查现有 ICollection<...>
的单元测试可能除外
引用自 @Ogglas answer of this post, 请问EF再生成一个table是否正常?
如果附加的 table 不应该存在,那我做错了什么?请赐教。 TIA!
示例代码:
public class Aggregate
{
public Aggregate()
{
Episodes = new HashSet<Episode>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
...
public virtual ICollection<Episode> Episodes { get; set; }
}
public class Episode
{
public Episode()
{
Aggregates = new HashSet<Aggregate>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
...
public virtual ICollection<Aggregate> Aggregates { get; set; }
}
public class EpisodeAggregate
{
[Key]
[Column(Order = 1)]
[ForeignKey("Episode")]
public Guid EpisodeId { get; set; }
[Key]
[Column(Order = 2)]
[ForeignKey("Aggregate")]
public Guid AggregateId { get; set; }
public virtual Episode Episode { get; set; }
public virtual Aggregate Aggregate { get; set; }
public DateTime Timestamp { get; set; }
}
在我的 DbContext.cs:
public DbSet<EpisodeAggregate> EpisodeAggregates { get; set; }
你是对的。在关系数据库中,使用联结 table.
解决多对多关系对于标准的多对多关系,你不需要提到这个连接点table; entity framework 通过在多对多关系的两边使用 virtual ICollection<...>
来识别多对多,并会自动为您创建 table。
为了测试我的数据库理论和 entity framework,我经常使用包含学校、学生和教师的简单数据库。学校-学生和学校-教师一对多,教师-学生多对多。我总是看到 Teacher-Student junction table 是自动创建的,无需提及它。
不过!
您的连接 table 不标准。标准联结 table 只有两列:EpisodeId
和 AggregateId
。它甚至没有额外的主键。组合 [EpisodeId, AggregateId] 已经是唯一的,可以用作主键。
您在 table EpisodeAggregate
中有一个额外的列:TimeStamp
。显然你想知道 Episode 和 Aggregate 何时相关。
“给我 Episode[4] 与 Aggregate[7] 关联时的时间戳”
这使得这个 table 不是标准的交汇点 table。 Episodes 和 Aggregates 之间没有多对多关系。您在 Episode 及其与聚合的关系之间建立了一对多关系,同样在聚合及其与 Episodes 的关系之间建立了一对多关系。
这使得您必须将多对多更改为一对多:
class Episode
{
public Guid Id { get; set; }
// every Episode has zero or more Relations with Aggregates (one-to-many)
public virtual ICollection<EpisodeAggregateRelation> EpisodeAggregateRelations { get; set; }
...
}
class Aggregate
{
public Guid Id { get; set; }
// every Episode has zero or more Relations with Episodes(one-to-many)
public virtual ICollection<EpisodeAggregateRelation> EpisodeAggregateRelations { get; set; }
...
}
class EpisodeAggregateRelation
{
// Every Relation is the Relation between one Episode and one Aggregate
// using foreign key:
public Guid EpisodeId { get; set; }
public Guid AggregateId { get; set; }
public virtual Episode Episode { get; set; }
public virtual Aggregate Aggregate { get; set; }
public DateTime Timestamp { get; set; }
}
如果您确定 Episode 和 Aggregate 之间始终最多只有一种关系,则可以使用组合 [EpisodeId, AggregateId] 作为主键。如果你认为这两个可能有几个关系,你需要添加一个单独的主键。
我经常在不同的数据库中使用我的类,因此我不喜欢属性,我在OnModelCreating中用流利的API解决它:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Episode>()
.HasKey(episode => episode.Id)
// define the one-to-many with EpisodeAggregateRelations:
.HasMany(episode => episode.EpisodeAggregateRelations)
.WithRequired(relation => relation.Episode)
.HasForeignKey(relation => relation.EpisodeId);
modelBuilder.Entity<Aggregate>()
.HasKey(aggregate => aggregate.Id)
// define the one-to-many with EpisodeAggregateRelations:
.HasMany(aggregate => aggregate .EpisodeAggregateRelations)
.WithRequired(relation => relation.Aggregate)
.HasForeignKey(relation => relation.aggregateId);
以上不需要!
因为你遵循了entity framework code first conventions,你可以省略这两个语句。 Entity framework 将识别主键和一对多关系。仅当您想要偏离约定时,例如非标准 table 名称,或者如果您想要定义列顺序:
modelBuilder.Entity<Episode>()
.ToTable("MySpecialTableName")
.Property(episode => episode.Date)
.HasColumnName("FirstBroadcastDate")
.HasColumnOrder(3)
.HasColumnType("datetime2");
但再次声明:您遵循了约定,不需要所有那些属性,如 Key、ForeignKey、DatabaseGenerated。和列顺序:谁在乎?让您的数据库管理系统决定最佳的列顺序。
我的建议是:尝试进行实验:忽略这种流利的方式 API 并检查您的单元测试是否仍然通过。五分钟后检查。
EpisodeAggregateRelation
有一些不标准的东西:它有一个复合主键。因此你需要定义这个。参见 Configuring a composite primary key
modelBuilder.Entity<EpisodeAggregateRelation>()
.HasKey(relation => new
{
relation.EpisodId,
relation.AggregateId
});
如果您已经在 Episodes 和 Aggregates 中定义了一对多关系,或者如果由于约定不需要它,则不必在这里再次提及这种关系。
如果需要,您可以将一对多放在 EpisodeAggregateRelation 的流畅 API 部分,而不是 Episode / Aggregate 的流畅 API 部分:
// every EpisodeAggregateRelation has one Episode, using foreign key
modelBuilder.Entity<EpisodeAggregateRelation>()
.HasRequired(relation => relation.Episode(
.WithMany(episode => episode.EpisodeAggregateRelations)
.HasForeignKey(relation => relation.EpisodeId);
// similar for Aggregate
最后一个提示
不要在构造函数中创建 HashSet。如果您获取数据,这是一种处理能力的浪费:您创建了 HashSet,它立即被 entity framework 创建的 ICollection<...>
所取代。
如果您不相信我:请尝试一下,看看您的单元测试是否通过,检查现有 ICollection<...>