一对一使用entity framework代码的可选关系
我们希望使用entity framework代码优先使用一对一的可选关系。 我们有两个实体。
public class PIIUser { public int Id { get; set; } public int? LoyaltyUserDetailId { get; set; } public LoyaltyUserDetail LoyaltyUserDetail { get; set; } } public class LoyaltyUserDetail { public int Id { get; set; } public double? AvailablePoints { get; set; } public int PIIUserId { get; set; } public PIIUser PIIUser { get; set; } }
PIIUser可能具有LoyaltyUserDetail,但LoyaltyUserDetail必须具有PII用户。 我们尝试了这些stream利的方法。
modelBuilder.Entity<PIIUser>() .HasOptional(t => t.LoyaltyUserDetail).WithOptionalPrincipal(t => t.PIIUser) .WillCascadeOnDelete(true);
这种方法没有在PIIUsers表中创buildLoyaltyUserDetailId外键。
之后,我们尝试了下面的代码。
modelBuilder.Entity<LoyaltyUserDetail>().HasRequired(t => t.PIIUser).WithRequiredDependent(t => t.LoyaltyUserDetail);
但是这次EF没有在这两个表中创build任何外键。
你对这个问题有什么想法吗? 我们如何使用entity framework代码首先创build一对一的可选关系?
EF Code First支持1:1
和1:0..1
关系。 后者是你正在寻找的(“一到零”)。
你的stream利的尝试在两种情况下都要求两种情况,另一种情况是两种情况都是可选的。
你需要的是一端是可选的 ,另一端是需要的。
这是一个编程EF代码优先书的例子
modelBuilder.Entity<PersonPhoto>() .HasRequired(p => p.PhotoOf) .WithOptional(p => p.Photo);
PersonPhoto
实体有一个名为PhotoOf
的导航属性,指向一个Person
types。 Person
types有一个名为Photo
的导航属性,指向PersonPhoto
types。
在两个相关的类中,您使用每个types的主键 ,而不是外键 。 即,您将不会使用LoyaltyUserDetailId
或PIIUserId
属性。 相反,关系取决于两种types的Id
字段。
如果你使用上面的stream畅的API,你不需要指定LoyaltyUser.Id
作为外键,EF将会把它弄清楚。
所以没有你的代码来testing自己(我讨厌从头开始)…我会把它翻译成你的代码
public class PIIUser { public int Id { get; set; } public LoyaltyUserDetail LoyaltyUserDetail { get; set; } } public class LoyaltyUserDetail { public int Id { get; set; } public double? AvailablePoints { get; set; } public PIIUser PIIUser { get; set; } } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<LoyaltyUserDetail>() .HasRequired(lu => lu.PIIUser ) .WithOptional(pi => pi.LoyaltyUserDetail ); }
这就是说LoyaltyUserDetails PIIUser
属性是必需的 ,PIIUser的LoyaltyUserDetail
属性是可选的。
你可以从另一端开始:
modelBuilder.Entity<PIIUser>() .HasOptional(pi => pi.LoyaltyUserDetail) .WithRequired(lu => lu.PIIUser);
现在说PIIUser的LoyaltyUserDetail
属性是可选的,LoyaltyUser的PIIUser
属性是必需的。
你总是必须使用HAS / WITH模式。
HTH和FWIW,一对一(或一对零/一)关系是首先在代码中configuration最混乱的关系之一,所以你并不孤单! 🙂
我们你最初的代码,
就像LoyaltyUserDetail
和PIIUser
之间有一对多的关系LoyaltyUserDetail
,那么你的映射应该是
modelBuilder.Entity<LoyaltyUserDetail>() .HasRequired(m => m.PIIUser ) .WithMany() .HasForeignKey(c => c.LoyaltyUserDetailId);
EF应该创build你需要的所有外键,只是不关心WithMany !
你的代码有几个错误。
1:1关系是: PK <-PK ,其中一个PK侧也是FK,或者PK <-FK + UC ,其中FK侧是非PK并且具有UC。 你的代码显示你有FK <-FK ,因为你定义双方有一个FK,但这是错误的。 我PIIUser
是PK方和LoyaltyUserDetail
是FK方。 这意味着PIIUser
不具有FK字段,但是LoyaltyUserDetail
具有。
如果1:1关系是可选的,则FK端必须至less有1个可为空的字段。
上面的pswg确实回答了你的问题,但却犯了一个错误,他/她还在PIIUser中定义了一个FK,这当然是错误的,正如我上面所描述的那样。 因此,定义LoyaltyUserDetail
的可空FK字段,在LoyaltyUserDetail
中定义属性以将其标记为FK字段,但是不在PIIUser
指定FK字段。
你会得到你在pswg的post下面描述的例外,因为没有任何一方是PK方(原则结束)。
EF在1:1方面并不是很好,因为它无法处理独特的限制。 我不是Code的第一个专家,所以我不知道它是否能够创build一个UC。
(编辑)btw: 1:1 B(FK)意味着只有1个FK约束创build,B指向A的PK的目标不是2。
尝试添加LoyaltyUserDetail
属性的ForeignKey
属性:
public class PIIUser { ... public int? LoyaltyUserDetailId { get; set; } [ForeignKey("LoyaltyUserDetailId")] public LoyaltyUserDetail LoyaltyUserDetail { get; set; } ... }
和PIIUser
属性:
public class LoyaltyUserDetail { ... public int PIIUserId { get; set; } [ForeignKey("PIIUserId")] public PIIUser PIIUser { get; set; } ... }
public class User { public int Id { get; set; } public int? LoyaltyUserId { get; set; } public virtual LoyaltyUser LoyaltyUser { get; set; } } public class LoyaltyUser { public int Id { get; set; } public virtual User MainUser { get; set; } } modelBuilder.Entity<User>() .HasOptional(x => x.LoyaltyUser) .WithOptionalDependent(c => c.MainUser) .WillCascadeOnDelete(false);
这将解决REFERENCE和FOREIGN KEYS上的问题
更新或删除logging时