使用EF Code First的多对多关系

本文关键字:关系 First EF Code 使用 | 更新日期: 2023-09-27 18:03:38

我定义了两个类:

public class Questionnaire
    {
        public int QuestionnaireID { get; set; }
        public string Title { get; set; }
        public bool Active { get; set; }
        public virtual ICollection<Question> Questions { get; set; }
        public virtual ICollection<Vendor> Vendors { get; set; }
    }
public class Vendor
    {
        public int VendorID { get; set; }
        public string VendorName { get; set; }
        public virtual ICollection<Questionnaire> OpenQuestionnaires { get; set; }
        public virtual ICollection<Questionnaire> SubmittedQuestionnaires { get; set; }
        public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
    }

我相信这是在这些类之间建立多对多关系的正确方法,并且当构建项目时,我希望创建三个表。

然而,当我试图将一个问卷关联到两个不同的供应商时,我在试图保存更改(context.SaveChanges())时收到以下错误:

*违反了多重性约束。关系"QuestionnaireApp.Models"的角色"Vendor_OpenQuestionnaires_Source"。vendor_openquestionnaire '具有多重性1或0..1.*

如果我只将问卷分配给一个供应商,保存更改,然后将其分配给另一个供应商并再次保存更改,我将不再得到错误;然而,问卷只与分配给它的最后一个供应商相关,这表明(充其量)正在创建一对多关系。

我希望我声明这些类之间的多对多关系的方式有问题,或者可能有一些我需要添加到上下文类以"鼓励"关系,但也许像这样的多对多关系是不支持的,或者不能使用"代码优先"创建?

感谢您的宝贵时间,

杰森

使用EF Code First的多对多关系

如果你没有任何Fluent API代码,你期望的映射依赖于EF code First约定。你期望在这里开始的惯例是AssociationInverseDiscoveryConvention。现在如果你看智能感知(可能还有文档)它说这个约定:

约定检测导航属性是否为每个属性的逆中只存在一对导航属性相关类型 .

现在,这就是问题所在:您在QuestionnaireVendor之间不只有"一对"导航属性。您在Vendor中有两个集合引用Questionnaire,在Questionnaire中有一个集合引用Vendor。结果是这个约定没有得到应用,EF实际上映射了三个一对多关系,只有一端在模型中作为导航属性公开。

此外,您想要实现的映射在您的模型中是不可能的:您不能将一端 Questionnaire.Vendors映射到两端 Vendor.OpenQuestionnairesVendor.SubmittedQuestionnaires

一种变通方法是按以下方式更改模型:

public class Vendor
{
    public int VendorID { get; set; }
    public string VendorName { get; set; }
    [NotMapped]
    public IEnumerable<Questionnaire> OpenQuestionnaires
    {
        get { return Questionnaires.Where(q => q.IsActive); }
    }
    [NotMapped]
    public IEnumerable<Questionnaire> SubmittedQuestionnaires
    {
        get { return Questionnaires.Where(q => !q.IsActive); }
    }
    public virtual ICollection<Questionnaire> Questionnaires { get; set; }
    public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}

现在,Vendor.Questionnaires被映射到Questionnaire.Vendors (AssociationInverseDiscoveryConvention应该检测到这一点),并且助手属性OpenQuestionnairesSubmittedQuestionnaires允许您拉出所选的项目。(我不确定IsActive是否是你的标志。否则你必须引入一些新的标志)

[NotMapped]属性在这里只是为了使其显式。这可能是不必要的,因为EF不会将IEnumerable集合和只读属性映射为只有getter。

想想看,经过一个小时左右的搜索,我在发帖后30秒就找到了确切的答案。

解决方案是将以下内容添加到上下文类中:
modelBuilder.Entity<Vendor>()
                .HasMany<Questionnaire>(x => x.OpenQuestionnaires)
                .WithMany(x => x.Vendors)
                .Map(x =>
                    {
                        x.MapLeftKey("vID");
                        x.MapRightKey("qID");
                        x.ToTable("VendorQuestionnaires");
                    });

我通过阅读这篇Stack Overflow post找到了答案:EF Code First多对多不工作