如何仅与一个实体创建多对多关系?

时间:2014-08-28 17:36:34

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

课程有许多先决条件,同时特定课程可以成为许多课程的先决条件。我尝试使用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.

此外,这是课程课程:

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:Name:类型中的每个属性名称必须是唯一的。属性   名称'ModuleId'已经定义。

     

CourseCourse:EntityType:EntitySet'TourseCourse'基于类型   'CourseCourse'没有定义键。

     

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

我找不到这样做的例子,这让我相信以下三个中的一个是真的:

  1. 有一种不同的方法来实现这一点,我没有看到
  2. 由于我对EF
  3. 的认识不足,我在正确的轨道上却忽略了一些东西
  4. 我是第一个尝试,EF不支持这种情况(非常不可能)
  5. 首先,有没有人知道如何建立这种关系,即这些错误意味着什么(响应#2)?对于奖励积分,是否有另一种方式可以做得更好或更差(有点#1)?提前谢谢。

2 个答案:

答案 0 :(得分:1)

我会像这样建模。我知道你只想要一张桌子。但如果你不这样做,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);

答案 1 :(得分:1)

您的映射几乎是正确的。但是你必须明白,实体框架会创建一个如此调用的联结表来存储多对多关系。

这个联结表只有两个字段,包含组成主键的外键。显然这些外键不能具有相同的名称.EF足够聪明,可以自行解决,不需要maping。下面是一个工作示例:

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"));
    }

的Presto!