一对一关系是Entity Framework 中很复杂的关系,涉及了 HasOptional 、WithRequired 、 WithOptionalPrincipal 、 WithOptionalDependent。这篇文章我们将具体讲解这几个的用法。
我们以会员和订单为例,一个会员有可能有订单,也可能没有订单,但是一个订单绝对属于一个会员。我们先编写出会员和订单的类代码:
public class Member { public int Id { get; set; } public string Name { get; set; } public virtual Order Order { get; set; } } public class Order { public int Id { get; set; } public int Name { get; set; } public int Num { get; set; } public virtual Member Member { get; set; } }
零、 HasOptionl then WithRequired
这种方式的会员和订单的映射类如下:
public class MemberMap : EntityTypeConfiguration<Member> { public MemberMap() { ToTable("Member"); HasKey(p => p.Id); Property(p => p.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasOptional(p => p.Order).WithRequired(p => p.Member); } } public class OrderMap : EntityTypeConfiguration<Order> { public OrderMap() { ToTable("Order"); HasKey(p => p.Id); Property(p => p.Id).HasColumnName("MemberId") .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); } }
这里将 Member 类的Id设为自增长,将 Order 的Id设置别名 MemberId 且非自增长。编写晚上下文类和调用类后,运行代码后,我们在数据库中将看到如下图:
其中 MemberId 就是在 OrderMap 中设置的别名
一、 HasOptionl then WithOptionalPrincipal
现在我们修改一下 MemberMap 和 OrderMap ,将 Member 和 Order 的Id都设置成自增长。
public class MemberMap : EntityTypeConfiguration<Member> { public MemberMap() { ToTable("Member"); HasKey(p => p.Id); Property(p => p.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasOptional(p => p.Order).WithOptionalPrincipal(p => p.Member); } } public class OrderMap : EntityTypeConfiguration<Order> { public OrderMap() { ToTable("Order"); HasKey(p => p.Id); Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); } }
再次执行代码后,查看生成的数据库:
我们看到 Order 表中 Member_Id 字段是EF自动生成的外键,且不可为空
二、 HasOptionl then WithOptionalDependent
再次修改 MemberMap 和 OrderMap :
public class MemberMap : EntityTypeConfiguration<Member> { public MemberMap() { ToTable("Member"); HasKey(p => p.Id); Property(p => p.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasOptional(p => p.Order).WithOptionalDependent(p => p.Member); } } public class OrderMap : EntityTypeConfiguration<Order> { public OrderMap() { ToTable("Order"); HasKey(p => p.Id); Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); } }
保存代码,运行程序,查看数据库:
和上一小节生成的数据库相比,这一小节生成的数据库 Member 表中自动生成了 Order 表的外键 Order_Id ,而 Order 表没有生成任何外键。
注:使用 WithOptionalPrincipal 可以使实体作为主体,将包含关系主键。使用 WithOptionalDependent 可以使实体作为以来提,将包含关系的外键。
前面所讲的都是从 Member 入手,我们同样也可以从 Order 表入手,但是在实际开发中我不建议这么做。下面就来说一下从 Order 入手的方法。
三、 HasRequired then WithOptional
同样我们修改 MemberMap 和 OrderMap :
public class MemberMap : EntityTypeConfiguration<Member> { public MemberMap() { ToTable("Member"); HasKey(p => p.Id); Property(p => p.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); } } public OrderMap() { ToTable("Order"); HasKey(p => p.Id); Property(p => p.Id).HasColumnName("MemberId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasRequired(p => p.Member).WithOptional(p => p.Order); }
这种方法生成的数据库与第一种方法结果一样。
四、 HasRequired 和 WithOptional
我们最后一次修改 MemberMap 和 OrderMap :
public class MemberMap : EntityTypeConfiguration<Member> { public MemberMap() { ToTable("Member"); HasKey(p => p.Id); Property(p => p.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); } } public class OrderMap : EntityTypeConfiguration<Order> { public OrderMap() { ToTable("Order"); HasKey(p => p.Id); Property(p => p.Id).HasColumnName("MemberId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); HasRequired(p => p.Member).WithOptional(p => p.Order).Map(p => p.MapKey("Member_Id"))) } }
这种方法生成的数据库与第二种方法结果一样。