使用EF Code First的多对多关系
我有两个类定义如下:
public class Questionnaire { public int QuestionnaireID { get; set; } public string Title { get; set; } public bool Active { get; set; } public virtual ICollection Questions { get; set; } public virtual ICollection Vendors { get; set; } } public class Vendor { public int VendorID { get; set; } public string VendorName { get; set; } public virtual ICollection OpenQuestionnaires { get; set; } public virtual ICollection SubmittedQuestionnaires { get; set; } public virtual ICollection QuestionnaireUsers { get; set; } }
我相信这是在这些类之间建立多对多关系的正确方法,并且在构建项目时,我希望创建三个表。
但是,当我尝试将一个调查问卷与两个不同的供应商联系起来时,在尝试保存更改时会收到以下错误(context.SaveChanges()):
*违反了多重约束。 ‘QuestionnaireApp.Models.Vendor_OpenQuestionnaires’关系的’Vendor_OpenQuestionnaires_Source’角色具有多重性1或0..1。*
如果我只将调查表分配给一个供应商,保存更改然后将其分配给另一个并再次保存更改我不再收到错误; 然而,问卷调查仅与其分配的最后一个供应商相关,表明(充其量)创建了一对多关系。
我希望我在声明这些类之间的多对多关系的方式有问题,或者我需要添加到上下文类中来“鼓励”关系,但也许很多这样的多对象关系不受支持,或者无法使用“Code First”创建?
感谢您的时间,
贾森
如果您没有任何Fluent API代码,则您期望的映射依赖于EF Code First约定。 您期望在这里启动的约定是AssociationInverseDiscoveryConvention
。 现在,如果您查看Intellisense(可能还有文档),它会说明这个约定:
当相关类型之间仅存在一对导航属性时 ,检测导航属性为彼此的反转的惯例。
现在,这就是问题: Questionnaire
和Vendor
之间没有“一对”导航属性。 Vendor
有两个集合参考Questionnaire
, Questionnaire
一个集合参考Vendor
。 结果是该约定未被应用,并且EF映射实际上是三个一对多关系,其中仅一个末端在模型中作为导航属性公开。
此外,您希望实现的映射不适用于您的模型:您无法将一端 Questionnaire.Vendors
映射到两端 Vendor.OpenQuestionnaires
和Vendor.SubmittedQuestionnaires
。
一种解决方法是通过以下方式更改模型:
public class Vendor { public int VendorID { get; set; } public string VendorName { get; set; } [NotMapped] public IEnumerable OpenQuestionnaires { get { return Questionnaires.Where(q => q.IsActive); } } [NotMapped] public IEnumerable SubmittedQuestionnaires { get { return Questionnaires.Where(q => !q.IsActive); } } public virtual ICollection Questionnaires { get; set; } public virtual ICollection QuestionnaireUsers { get; set; } }
现在Vendor.Questionnaires
映射到Questionnaire.Vendors
( AssociationInverseDiscoveryConvention
应检测到这一点),帮助属性OpenQuestionnaires
和SubmittedQuestionnaires
允许您拉出所选项目。 (我不确定IsActive
是否是你的区别标志。否则你必须引入一些新的旗帜。)
[NotMapped]
属性就是为了使它明确。 它可能没有必要,因为EF无论如何都不会仅使用getter来映射IEnumerable
集合和只读属性。
去看看,经过一个小时左右的搜索,我在发布问题30秒后找到确切的答案。
解决方案是将以下内容添加到上下文类中:
modelBuilder.Entity() .HasMany(x => x.OpenQuestionnaires) .WithMany(x => x.Vendors) .Map(x => { x.MapLeftKey("vID"); x.MapRightKey("qID"); x.ToTable("VendorQuestionnaires"); });
我通过阅读Stack Overflowpost找到了答案: EF Code First多对多无法正常工作