天哪,这是一个奇怪的多对多对多!
通常会有以下设计:
- 每个
Product 有零个或多个Parts,每个Part 用于零个或多个Products(多对多ProductPart)
- 每个
Product 有零个或多个Enumerations,每个Enumeration 被零个或多个Products 使用(多对多ProductEnumeration)
- 每个
Part 有零个或多个Enumerations,每个Enumeration 用于零个或多个Parts(多对多EnumerationPart)
这将导致三个联结表ProductPart、ProductEnumeration 和EnumerationPart
您选择了一个只有一个接线表的设计。
您确实意识到,在您的设计中,只要Product 具有Part,Product 和Part 都具有相同的Enumeration,不是吗? product 不能比 product 的 Parts 更多或其他 Enumerations。 Product 不能有Part 但没有Enumerations。
如果你真的想把你的数据库限制在这个范围内,你会有这样的类:
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Junction> Junctions { get; set; }
}
class Part
{
public int Id { get; set; }
public int Number { get; set; }
public string Description { get; set; }
public virtual ICollection<Junction> Junctions { get; set; }
}
class Enumeration
{
public int Id { get; set; }
public int Code { get; set; }
public virtual ICollection<Junction> Junctions { get; set; }
}
您的联结表将如下所示:
public class Junction
{
// foreign keys; composite primary key
public int ProductId { get; set; }
public int EnumId { get; set; }
public int PartId { get; set; }
public virtual Product Product { get; set; }
public virtual Enumeration Enumeration { get; set; }
public virtual Part Part { get; set; }
}
注意:在实体框架中,非虚拟属性代表
表中的实际列;虚拟属性代表
表之间的关系
您的 DbContext 将包含您的四个表:
class ManyDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Enumeration> Enumerations { get; set; }
public DbSet<Part> Parts { get; set; }
public DbSet<Junction> Junctions {get; set;}
}
最后,您必须在 OnModelCreating 中指定您的设计:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var junctionConfig = modelBuilder.Entity<Junction>();
// composite primary key:
junctionConfig.HasKey(junc => new { junc.ProductId, junc.PartId, junc.EnumId);
// every junctionconfig has a mandatory foreign key to Product in ProductId
// which represent a one-to-many (one Product has many Junctions)
junctionConfig.HasRequired<Product>(junc => junc.Product)
.WithMany(product => product.Junctions)
.HasForeignKey<int>(junc => junc.ProductId);
// every junctionconfig has a mandatory foreign key to Enumerations in EnumerationId
// which represent a one-to-many (one Enumeration has many Junctions)
junctionConfig.HasRequired<Enumeration>(junc => junc.Enumeration)
.WithMany(enumeration => enumeration.Junctions)
.HasForeignKey<int>(junc => junc.EnumId);
// every junctionconfig has a mandatory foreign key to Pars in PartId
// which represent a one-to-many (one Part has many Junctions)
junctionConfig.HasRequired<Part>(junc => junc.Part)
.WithMany(part => part.Junctions)
.HasForeignKey<int>(junc => junc.PartId);
base.OnModelCreating(modelBuilder);
}
回到你的问题
给定一个productId,给我所有EnumerationCodes、Part.Number、Part.Description 的记录Product 和这个ProductId
当使用实体框架时,人们倾向于在外键上执行连接,而不是在表中使用虚拟属性,这使得事情变得比需要的更复杂。
如果您使用虚拟属性,查询将非常简单且非常直观:
var result = dbContext.Junctions
.Where(junction => junction.ProductId == productId)
.Select(junction => new
{
EnumerationCode = junction.Enumeration.EnumerationCode,
PartNumber = junction.Part.Number,
PartDescription = junction.Part.Description,
});
实体框架足够智能,可以检测需要哪些连接。
如果您真的想进行联接,则必须对三个表进行联接。这样的连接看起来很可怕:
var x = dbContext.Junctions // from all junctions
.Where(junction => junction.ProductId == productId) // take only the ones with productId
// The first join:
.Join(dbContext.Parts, // Join with Parts
junction => junction.PartId, // from the Junction take the PartId,
part => part.Id, // from the Parts take the Id
(junction, part) => new // when they match make one new object
{ // containing some properties
EnumerationId = junction.EnumId,
PartNumber = part.Number,
PartDescription = part.Description,
})
// Second Join
.Join(dbContext.Enumerations, // Join with enumerations
junction => junction.EnumerationId, // from the 1st join result take EnumerationId
enumeration => enumeration.Id, // from enumerations take Id
(junction, enumeration) => new // when they match make one new object
{ // containing your desired properties
EnumerationCode = enumeration.Code,
PartNumber = junction.PartNumber,
PartDescription = junction.PartDescription,
});
您很幸运,您不需要产品的描述。如果您使用虚拟属性,这将很容易:
var result = dbContext.Junctions
.Where(junction => junction.ProductId == productId)
.Select(junction => new
{
Description = junction.Product.Description,
EnumerationCode = junction.Enumeration.EnumerationCode,
PartNumber = junction.Part.Number,
PartDescription = junction.Part.Description,
});
由您来编写包含四个表的连接。查看差异并决定从现在开始使用哪种方法。