使用EF Code First的多对多关系

时间:2011-07-27 19:14:42

标签: c# .net entity-framework asp.net-mvc-3 ef-code-first

我有两个定义的类:

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'关系的'Vendor_OpenQuestionnaires_Source'角色具有多重性1或0..1。*

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

我希望我在声明这些类之间的多对多关系的方式有问题,或者我需要在上下文类中添加一些内容以“鼓励”关系,但是,这样的多对多关系可能不受支持,或者无法使用“Code First”创建?

感谢您的时间,

杰森

2 个答案:

答案 0 :(得分:2)

如果您没有任何Fluent API代码,则您预期的映射依赖于EF Code First约定。您希望在此处启用的约定是AssociationInverseDiscoveryConvention。现在,如果您查看Intellisense(可能还有文档),它会说明这个约定:

  

检测导航属性的惯例是每个的反转   其他当两者之间只存在一对导航属性时   相关类型

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

此外,您的模型无法实现您想要实现的映射:您无法将一端 Questionnaire.Vendors映射到两端 {{1} }和Vendor.OpenQuestionnaires

一种解决方法是按以下方式更改模型:

Vendor.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.QuestionnairesQuestionnaire.Vendors应检测到这一点),帮助属性AssociationInverseDiscoveryConventionOpenQuestionnaires允许您提取所选项目。 (我不确定SubmittedQuestionnaires是否是你的区别标志。否则你必须引入一些新的旗帜。)

IsActive属性就是为了让它显而易见。这可能没有必要,因为EF不会仅使用getter映射[NotMapped]集合和只读属性。

答案 1 :(得分:1)

去看看,经过一个小时左右的搜索,我会在发布问题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帖子找到了答案:EF Code First Many-to-Many not working