我正在尝试使用代码创建一对多和反向一对一的关系。这是我想做的事情
1)两个类之间的一对多,它按预期工作。
public class X
{
[Key]
public int XId { get; set; }
public ICollection<Y> Y { get; set; }
}
public class Y
{
[Key]
public int YId { get; set; }
public int XId { get; set; }
public X X { get; set; }
}
public class DataContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Y>()
.HasRequired(y => y.X)
.WithMany(x => x.Y)
.HasForeignKey(y => y.XId);
}
}
现在我要做的是在Y和X之间创建Reverse One-to-One可选关系,这样X将包含Y的外键......怎么可能?这是我想要做的,它会引发一些多重错误
public class X
{
[Key]
public int XId { get; set; }
public ICollection<Y> Y { get; set; }
public int YId {get; set; }
[ForiegnKey("YId")]
public Y YOptional { get; set; }
}
public class Y
{
[Key]
public int YId { get; set; }
public int XId { get; set; }
public X X { get; set; }
public X XOptional {get; set; }
}
public class DataContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Y>()
.HasRequired(y => y.X)
.WithMany(x => x.Y)
.HasForeignKey(y => y.XId);
modelBuilder.Entity<X>()
.HasOptional(x => x.YOptional)
.WithOptionalDependent(y=> y.XOptional);
}
}
答案 0 :(得分:5)
您不能在两个实体之间建立关系,这两个实体的定义与两端不同。所以你不能从一个方向做1:*而从另一个方向做1:1。
让我猜一下,你真的不希望它从依赖端1:1。从那一端起,它总是只指向一件事。
在映射中,与生活不同,除非你有多对多,否则一个孩子只有一个父母。
但是,您可以创建一个0..1:* relationaship(零或一对多)。如果父母可以有一个或多个孩子(例如&#34;很多&#34;),但孩子可以在没有父母的情况下存在,但它永远不会有多于一个父母(例如&#34;零或一个&#34; )。
这是使您的类产生[零或一个]到多个关系的最简单方法。请注意,我在类Y中使外键成为可以为null的int。通过此设置,EF约定将生成一个映射,允许子节点在没有父节点的情况下存在。
public class X
{
[Key]
public int XId { get; set; }
public ICollection<Y> Y { get; set; }
}
public class Y
{
[Key]
public int YId { get; set; }
public int? XId { get; set; }
public X X { get; set; }
}
public class DataContext : DbContext
{
public DbSet<X> XSet { get; set; }
public DbSet<Y> YSet { get; set; }
}
以下是从上述类和上下文派生的可视化模型的屏幕截图。 我认为,如果我猜测你可能只是以不同的方式感知它是正确的,那么这就达到了你所寻求的行为。
答案 1 :(得分:3)
使用您在评论中提到的实际类名:
映射可能包含多个User
的{{1}}不是问题。但是,当您想要在Single
和User
之间映射1:1关联时,您必须选择两者中的哪一个是“原则”实体。两个表中都不能有外键列,因为一个实体将始终插入另一个实体之前。接下来插入“依赖”实体,它引用主体的主键值。
因此,如果Single
是主要实体,则可以使用类似于此的类模型:
User
映射的两个选项:
public class User
{
public User()
{
this.Singles = new HashSet<Single>();
}
public int UserId { get; set; }
public string Name { get; set; }
public Single Single { get; set; }
public virtual ICollection<Single> Singles { get; set; }
}
public class Single
{
public int SingleId { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
public int SuperUserId { get; set; }
public User SuperUser { get; set; }
}
作为主要User
在数据模型中,protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Singles)
.WithRequired(s => s.SuperUser).HasForeignKey(s => s.SuperUserId);
modelBuilder.Entity<User>().HasOptional(s => s.Single)
.WithOptionalPrincipal(s => s.User).Map(m => m.MapKey("UserId"));
}
现在有两个外键,Single
和UserId
。这是在SuperUserId
和 User
中创建Single
和User.Single
的方法:
User.Singles
EF将首先插入var superUser = new User { Name = "superUser1" };
var single = new Single { Name = "single" };
superUser.Singles.Add(single);
db.Users.Add(superUser);
superUser.Single = single;
db.SaveChanges();
,然后User
将两个外键都等于Single
的主键。
User
原则您还可以将Single
作为1:1关联中的主体实体:
Single
现在modelBuilder.Entity<User>().HasOptional(s => s.Single)
.WithOptionalDependent(s => s.User).Map(m => m.MapKey("SingleId"));
(Single
)中只有一个外键,SuperUserId
(User
)中只有外键。如果你执行相同的代码,现在EF将抛出
无法确定相关操作的有效排序。
这是因为存在鸡与蛋问题:必须在创建依赖SingleId
之前创建Single
,但必须在User
之前创建User
{1}}可以添加到其Single
集合中。通过稍后分配Singles
Single
你想把它包装在var superUser = new User { Name = "superUser1" };
var single = new Single { Name = "single" };
superUser.Singles.Add(single);
db.Users.Add(superUser);
db.SaveChanges();
superUser.Single = single;
db.SaveChanges();
中,所以我认为这个选项不太可行。
注意
如您所见,在1:1映射中,外键无法映射到类模型中的属性。 TransactionScope
或HasForeignKey
之后的流畅API中没有WithOptionalDependent
。此外,此关联只能通过流畅的API进行映射。在数据注释中,没有属性来指示关联的主要结束。