根据您想要使用这种关系的方式,有几种方法可以实现这一点。存在与不同实现相关的性能问题。
例如,如果您需要对父母、兄弟和儿子列表进行大量快速查询并且对这些数据进行少量修改,您可以冗余存储信息,因此您有一个 Sons/Parents 表和一个 Brothers 表,与这些关系的一些 EF 映射。这些表只存储相关人员的主键,每次修改其中一个关系(例如,添加新儿子)时,都必须更新多个表中的相同信息。
但是,如果您需要对数据进行大量修改而不是进行大量查询,那么最好只存储 Parents 表,并且可以通过在代码,但不是通过 EF 映射。
带有冗余表的示例:使用这些映射:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>().HasMany(u => u.Parents).WithMany(u => u.Sons).Map( x => { x.MapRightKey("ParentId"); x.MapLeftKey("SonId"); x.ToTable("ParentsSons"); } );
modelBuilder.Entity<ApplicationUser>().HasMany(u => u.Sons).WithMany(u => u.Parents);
modelBuilder.Entity<ApplicationUser>().HasMany(u => u.Brothers).WithMany().Map(x => { x.MapRightKey("BrotherId"); x.MapLeftKey("Id"); x.ToTable("Brothers"); });
}
迁移包括这些表(为简单起见,我向 ApplicationsUsers 实体添加了一个整数 Id 属性):
CreateTable(
"dbo.ApplicationUsers",
c => new
{
Id = c.Int(nullable: false, identity: true),
FirstName = c.String(),
LastName = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Brothers",
c => new
{
Id = c.Int(nullable: false),
BrotherId = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.Id, t.BrotherId })
.ForeignKey("dbo.ApplicationUsers", t => t.Id)
.ForeignKey("dbo.ApplicationUsers", t => t.BrotherId)
.Index(t => t.Id)
.Index(t => t.BrotherId);
CreateTable(
"dbo.ParentsSons",
c => new
{
SonId = c.Int(nullable: false),
ParentId = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.SonId, t.ParentId })
.ForeignKey("dbo.ApplicationUsers", t => t.SonId)
.ForeignKey("dbo.ApplicationUsers", t => t.ParentId)
.Index(t => t.SonId)
.Index(t => t.ParentId);
使用这些表格玩一点的示例方法。如果您通过它进行调试,您可以检查实体并检查父母和儿子列表的内容。 TestContext 仅包含一个 DbSet 用户:public DbSet<ApplicationUser> Users { get; set; }
public void Test()
{
using (var context = new TestContext())
{
var arya = new ApplicationUser()
{
FirstName = "Arya",
Parents = new List<ApplicationUser>
{
new ApplicationUser { FirstName = "Eddard" },
new ApplicationUser { FirstName = "Catelyn" }
}
};
context.Users.Add(arya);
context.SaveChanges();
var eddard = context.Users.First(u => u.FirstName == "Eddard");
var jon = new ApplicationUser { FirstName = "Jon" };
eddard.Sons.Add(jon);
context.SaveChanges();
var eddardFromContext = context.Users.First(u => u.FirstName == "Eddard");
var catelynFromContext = context.Users.First(u => u.FirstName == "Catelyn");
}
}
我将兄弟列表排除在示例之外。请注意,EF 不会自动将 Brothers 数据与父母/儿子数据同步。因此,在将用户添加到父母/儿子列表时,您必须明确地将用户添加到兄弟列表中。但是父母和儿子列表是自动同步的:如果您将用户 A 添加到用户 B 的父母列表并持久化数据,则用户 B 的父母列表将包含用户 A。这是因为父母和儿子的映射使用相同的导航属性。 Brothers 的财产不会发生这种情况。