【问题标题】:How do I create a Many-to-Many relationship with only one entity?如何仅与一个实体创建多对多关系?
【发布时间】:2014-08-29 06:07:30
【问题描述】:

课程有许多先决条件,同时一门特定课程可能是许多课程的先决条件。我尝试使用 EF 代码优先建立多对多关系(在 OnModelBCreating 中),其中包含以下内容:

modelBuilder.Entity<Course>()
                .HasMany(e => e.Prerequisites)
                .WithMany(e => e.Postrequisites)
                .Map(m => m.ToTable("CourseRequisiteMappings")
                .MapLeftKey("CourseId").MapRightKey("CourseId")); // EDIT: THIS LINE IS THE PROBLEM. SEE MARKED ANSWER AND MY COMMENT ON IT.

另外,这里是 Course 类:

public class Course
{
    public int CourseId { get; set; }
    public string Name { get; set; }
    public string InstitutionCode { get; set; }     
    public string Description { get; set; }
    public bool IsElective { get; set; }
    public virtual ICollection<Instructor> Instructors { get; set; }
    public virtual ICollection<Student> Students { get; set; }
    public virtual ICollection<Module> Modules { get; set; }
    public virtual ICollection<Course> Prerequisites { get; set; }
    public virtual ICollection<Course> Postrequisites { get; set; }
}

当我实现这个并去更新数据库时,它给了我以下错误:

CourseId:名称:类型中的每个属性名称必须是唯一的。财产 名称“CourseId”已定义。

ModuleId:名称:类型中的每个属性名称必须是唯一的。财产 名称“ModuleId”已定义。

CourseCourse: EntityType: EntitySet 'CourseCourse' 基于类型 没有定义键的“CourseCourse”。

ModuleModule: EntityType: EntitySet 'ModuleModule' 基于类型 没有定义键的“ModuleModule”。

我找不到这样做的例子,这使我相信以下三个之一是正确的:

  1. 我看不到有一种不同的方法来实现这一点
  2. 我走在正确的轨道上,但由于我对 EF 缺乏了解而忽略了一些事情
  3. 我是第一个尝试的,但 EF 不支持这个(不太可能)

首先,有谁知道我可以如何建立这种关系,即这些错误是什么意思(响应 #2)?对于奖励积分,是否有另一种可能更好或更差的方法(有点#1)?提前致谢。

【问题讨论】:

    标签: c# entity-framework ef-code-first


    【解决方案1】:

    您的映射几乎是正确的。但您必须了解,实体框架将在底层创建一个所谓的联结表,用于存储多对多关系。

    这个联结表只有两个字段,包含共同构成主键的外键。显然,这些外键不能具有相同的名称。EF 足够聪明,可以自己弄清楚,不需要映射。下面是一个工作示例:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Data.Entity;
    
    namespace ManyToManyUnderTheHoodSpike
    {
    class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<CourseContext>());
            using (CourseContext context=new CourseContext())
            {
                context.Courses.Add(new Course("Top of the bill")
                {
                    PrerequisiteCourses = new List<Course>() 
                        {
                            new Course("My two cents"),
                            new Course("Counting to two")
                        }
                });
                context.SaveChanges();
            }
        }
    }
    
    public class CourseContext : DbContext
    {
    
        public DbSet<Course> Courses { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }
    
    public class Course
    {
    
        public Course() { }
    
        public  Course(string name)
        {
            Name = name;
        }
    
        public string Name {get;set;}
        public int CourseId{get;set;}
    
        public ICollection<Course> PrerequisiteCourses{get;set;}
        public ICollection<Course> FollowUpCourses{get;set;}
    }
    }
    

    如果您运行此代码,您将获得一个包含两个表的数据库:CoursesCourseCourses,其中仅有字段 Course_IdCourse_Id1

    但这不是很可读,所以让我们制作映射以使其更具可读性:

     protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Course>().HasMany(course => course.PrerequisiteCourses)
                .WithMany(course => course.FollowUpCourses)
                .Map(data => data.ToTable("Prerequisites")
                    .MapLeftKey("FollowUpId")
                    .MapRightKey("PrerequisiteId"));
        }
    

    快!

    【讨论】:

    • 没错!我正要问可读性,你有它。因此,从字面上看,我唯一做错的事情就是对两个映射键都使用了名称“CourseId”。这就是问题所在:我在想“MapLeft/RighKey”方法想要旧表上的键列的名称,而不是新的中间表。最后,你不喜欢我新创造的术语“后置条件”吗? =P
    • 我总是在纠正别人的命名,一直认为我可以做得更好;-) “先决条件”确实是必需的。后续课程通常是可选的...
    • “必需”怎么样?
    【解决方案2】:

    我会这样建模。我知道你只想要一张桌子。但是如果你不这样做,Ef 会创建多对多表。不确定没有测试你没有做对什么。所以无论如何,这是另一种选择。

    public class Course
    {
    public int CourseId { get; set; }
    public string Name { get; set; }
    public string InstitutionCode { get; set; }     
    public string Description { get; set; }
    public bool IsElective { get; set; }
    //nav elements
    public virtual ICollection<Instructor> Instructors { get; set; }
    public virtual ICollection<Student> Students { get; set; }
    public virtual ICollection<Module> Modules { get; set; }
    public virtual ICollection<PreReqCourse> Prerequisites { get; set; }
    // You can Find follow on courses, by accessing PreReqCourse table, but if you felt this navigation offered enough value, create a post req table too. Using same approach.
    // public virtual ICollection<Course> Postrequisites { get; set; } 
    }
    
    public class PreReqCourse
    {
    public virtual int Id {get; set;}
    public virtual int CourseId { get; set; }
    public virtual Course  PreReqForCourse { get; set; } //Nav prop
    }
    
    
    modelBuilder.Entity<Course>()
                .HasMany(e => e.Prerequisites)
                .WithMany();   
    // Leave WithMany empty. You can define in PreReqCourse Table model, you dont need to model from both directions. 
    
    
    modelBuilder.Entity<PreReqCourse>()
                .HasRequired(e => e.PreReqForCourse)
                .HasForeignKey(f => f.CourseId)
                .WithMany(p=>p.PreRequisites);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-23
      • 1970-01-01
      • 2016-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多