Ken*_*Ken 0 c# linq entity-framework relationship winforms
我一直在NET和SO中寻找一些答案 - 我对实体框架几乎是一个菜鸟..所以我对相关实体确实有一些困惑..我正在使用 C# , Winforms , Linq , EF6^ (生成的反向数据库类在 DAL 项目中)
我很难解决这个问题。我有 3 个表,它们与第四个表相关 - 第 4 个表使用其他 3 个表中的 PK_ID 列 - 配置为外键,它们还组成了复合键(主键)。
我的粗略 ER 图。
我需要在 EF 查询中做的是获取多个列以将数据绑定绑定到列表视图。
我想要的列是 EnumerationCode、Part.Number、Part.Description。
我将使用 ProdID (我将 Products 绑定到 DropDown )作为这些附加值的 getter。
所以基本上我想到了这一点 Select From TernaryTable T Join EnumID on ENumTable.EnumID where T.ProdID = myProdID order by EnumCode [Join this to Select Part.Number, Part.Description From PartID = TernaryTable.PartID )
我认为可能有一个简单的解决方案来获取这些数据,然后如何保存对数据的任何更改 - 但正如我所说,我迷失了。
我需要两种方法,一种是获取数据,另一种是保存数据发生的任何更改。
我读过这个
但似乎我仍然迷失在多重关系中。
编辑
我认为在 Haralds 出色的答复之后,我需要并且可以发布更好的声明以及真实的图表!
现在查看表格信息:
我想要 MprnEnum.RefName、Part.PartNumber、Part.Name、Part.Description,其中 ProductID = [Product.ID - 我将提供的此 ID] 按 MPRNENUM.RefName 订购。我希望能够使用 VS EF 从数据库生成的实体类对 ProdPartEnum 执行 CRUD。
有 20 个 RefName,每个产品都有许多 EnumID,并且 EnumID 可以属于许多产品,这些 ProdEnum 中的每一个都有一个关联的部分。许多零件可以属于许多 ProdEnum
典型的表条目可能如下所示: ProdID = 1, EnumID = [1-20] , PartID [1-1000] 其中产品 ID = 1 ,我们有 20 行 EnumID,并且每一行都有一个 PartNumber 。
如果需要,我可以发布更多详细信息。
天哪,这真是奇怪的多对多对多!
通常情况下会有以下设计:
Product有零个或多个Parts,EveryPart用于零个或多个Products(多对多ProductPart)Product都有零个或多个Enumerations,每个Enumeration被零个或多个使用Products(多对多ProductEnumeration)Part有零个或多个Enumerations,EveryEnumeration用于零个或多个Parts(多对多EnumerationPart)这将导致三个联结表ProductPart, ProductEnumeration, 和EnumerationPart
您选择了只有一个连接表的设计。
您确实意识到,在您的设计中,一旦 aProduct有 a Part,则 theProduct和 thePart都具有相同的Enumeration,不是吗?除了the 之外,您不能有product更多或其他的a 。你不能有 a和 a但没有。EnumerationsPartsproductProductPartEnumerations
如果您确实想将数据库限制于此,您将拥有如下类:
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; }
}
Run Code Online (Sandbox Code Playgroud)
你的联结表将是这样的:
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; }
}
Run Code Online (Sandbox Code Playgroud)
请注意:在实体框架中,非虚拟属性代表表中的实际列;虚拟属性表示表之间的关系
您的 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;}
}
Run Code Online (Sandbox Code Playgroud)
最后在 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);
}
Run Code Online (Sandbox Code Playgroud)
给定 a ,
productId给我所有EnumerationCodes,Part.Number,Part.Description记录ProductProductId
当使用实体框架时,人们倾向于在外键上执行联接,而不是使用表中的虚拟属性,这使得事情变得更加复杂。
如果您使用虚拟属性,查询将变得简单且非常直观:
var result = dbContext.Junctions
.Where(junction => junction.ProductId == productId)
.Select(junction => new
{
EnumerationCode = junction.Enumeration.EnumerationCode,
PartNumber = junction.Part.Number,
PartDescription = junction.Part.Description,
});
Run Code Online (Sandbox Code Playgroud)
实体框架足够智能,可以检测需要哪些连接。
如果您确实想要进行连接,则必须与三个表进行连接。这样的连接看起来很可怕:
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,
});
Run Code Online (Sandbox Code Playgroud)
您很幸运,您不需要产品的说明。如果您使用虚拟属性,这会很容易:
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,
});
Run Code Online (Sandbox Code Playgroud)
由您决定编写四个表的联接。查看差异并决定从现在开始要使用哪种方法。