m:n关系中的EF 6访问密钥表

时间:2019-02-09 21:55:19

标签: entity-framework

我有一个具有n:m关系的EF 6代码优先模型。 “课程和兴趣”之间的表具有键 这个课程和这个兴趣。

我用

        modelBuilder.Entity<Course>()
            .HasMany(e => e.Interests)
            .WithMany(e => e.Courses)
            .Map(m => {
                m.ToTable("Interests2Courses");
                m.MapLeftKey("This2Course");
                m.MapRightKey("This2Interest");
            });


        modelBuilder.Entity<Interest>()
            .HasMany(e => e.Courses)
            .WithMany(e => e.Interests)
            .Map(m => {
                m.ToTable("Interests2Courses");
                m.MapLeftKey("This2Interest");
                m.MapRightKey("This2Course");
            });

我在课程中

        public virtual ICollection<Interests2Courses> Interests2Courses { get; set; }
    public virtual ICollection<Interest> Interests { get; set; }

对于“兴趣”集合来说效果很好。 但是当我尝试像这样访问(加载)链接表时:

Course cO = dbC.Courses.Include(a => a.Interests2Courses).FirstOrDefault(a => a.ShortName == "K1");

我得到一个例外:无效的对象名称dbo.Interests2Courses1

所以我只能访问“简单n:m”或链接表。 有办法访问两者吗?

1 个答案:

答案 0 :(得分:1)

因此,每个Course将具有零个或多个Interests,而每个Interest将具有零个或多个Courses。没错,在关系数据库中,这是使用联结表实现的。但是,您的实体框架是数据库的抽象,您不需要定义此联结表:

class Course
{
    public int Id {get; set;}
    ...

    // every Course has zero or more Interests (many-to-many)
    public virtual ICollection<Interest> Interests {get; set;}
}
class Interest
{
    public int Id {get; set;}
    ...

    // every Interest has zero or more Courses(many-to-many)
    public virtual ICollection<Course> Courses{get; set;}
}

为了完整起见,dbContext

class MyDbContext : DbContext
{
    public DbSet<Course> Courses {get; set;}
    public DbSet<Interest> Interests {get; set;}
}

这是实体框架识别表,表中的列以及多对多关系所需要了解的所有内容。尽管您没有提到联结表,但是实体框架将为您创建一个联结表。

不需要属性,也不需要流畅的API。仅当您对Entity Framework为您发明的默认标识符不满意时,才需要流利的API。

但是,如果我无法访问交汇表,该如何(分组)加入课程和兴趣?

回答:不要进行(group-)联接,请使用虚拟ICollections!

  

给我所有(或一些)与它的全部(或一些)兴趣相关的课程

var result = dbContext.Courses
    .Where(course => ...)       // only if you don't want all Courses
    .Select(course => new
    {
        // only select the properties you actually plan to to use
        Id = course.Id,
        Name = course.Name,
        ...

        Interests = course.Interests
            .Where(interest => ...)  // only if you don't want all its Interests
            .Select(interest => new
            {
                 Id = interest.Id,
                 ...
            })
            .ToList(),
    });

实体框架了解您的多对多关系,并将为您进行适当的组联接。或者当然,您可以反过来做:“给我他们课程的所有兴趣”

评论后添加

如果联结表不是纯联结表,因为它具有某些属性,则需要将其添加到DbContext中。在这种情况下,您必须添加一个代表此JunctionItem的类。

TODO:发明一个可以正确描述此JunctionItem表示的名称

请注意,您的课程与兴趣之间的多对多关系将变为课程与连接之间的一对多。结点与利益之间的关系也变为一对多。

class Course
{
    public int Id {get; set;}
    ...

    // every Course has zero or more JunctionItems (one-to-many)
    public virtual ICollection<JunctionItem> JunctionItems {get; set;}
}
class Interest
{
    public int Id {get; set;}
    ...

    // every Interest has zero or more JunctionItems(one-to-many)
    public virtual ICollection<JunctionItem> JunctionItems{get; set;}
}

新的JunctionItem类:

class JunctionItem
{
    public int Id {get; set;}
    ... // other JunctionItem properties

    // every JunctionItem belongs to exactly one Course (using foreign key)
    public int CourseId {get; set;}
    public virtual Course Course {get; set;}

    // every JunctionItem belongs to exactly one Interest (using foreign key)
    public int InterestId {get; set;}
    public virtual Interest Interest {get; set;}
}

和DbContext:

class MyDbContext : DbContext
{
    public DbSet<Course> Courses {get; set;}
    public DbSet<Interest> Interests {get; set;}

    public DbSet<JunctionItem> JunctionItems {get; set;}
}

由于您将关系定义为虚拟属性,因此实体框架已经检测到您的一对多关系,因此您不必将这些关系告知模型构建者。但是,如果您想要:

var junctionEntity = modelBuilder.Entity<JunctionItem>();

// every junctionEntity has one-to-many with Course:
junctionEntity.HasRequired(junction => junction.Course)
   .WithMany(course => course.JunctionEntities)
   .HasForeignKey(junction => junction.CourseId);

一些类似的事情:

junctionEntity.HasRequired(junction => junction.Interest)
    .WithMany(interest => interest.JunctionEntities)
    .HasForeignKey(junction => junction.InterestId);

注意:一种优化方法是将JunctionTable.CourseId和JunctionTable.InterestId组合到一个复合主键中:

junctionEntity.HasKey(junction => new {junction.CourseId, junction.InterestId});

这禁止两个单独的JunctionItems指向相同的(课程,兴趣)组合:

Course courseA = ...
Interest interestB = ...

// not possible:
JunctionInterest j1 = new JunctionInterest {Course = courseA, Interest = interestB};
JunctionInterest j2 = new JunctionInterest {Course = courseA, Interest = interestB};

如果需要,在JunctionItem中需要一个单独的主键。

您的查询将与纯粹的多对多查询相似,但现在它们是一对多查询。不使用join,使用ICollection:

var result = dbContext.Courses
    .Select(course => new
    {
        Id = course.Id
        Name = course.Name,
        ...

       JunctionItems = course.JunctionItems.Select(junctionItem => new
       {
            ... // junction item properties

            Interests = junctionItem.Interest.Select(interest => new
            {
                 ... // interest properties
            })
            .ToList(),
       })
       .ToList(),
    });
    });