我有一个具有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”或链接表。 有办法访问两者吗?
答案 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(),
});
});