【问题标题】:Entity Framework Related Tables Get and Save Data实体框架相关表获取和保存数据
【发布时间】:2018-08-21 02:40:07
【问题描述】:

我一直在搜索 NETSO 以寻找一些答案 - 我几乎是个菜鸟实体框架..所以我对相关实体确实有些困惑..我正在使用 C#、Winforms、Linq、EF6^(DAL 项目中生成的反向 DB 类)

我无法解决这个问题。我有 3 个表,它们与第四个表相关 - 第 4 个表使用其他 3 个表中的 PK_ID 列 - 配置为外键,它们还构成复合键(主键)。

我绘制的原始 ER 图。

我需要在 EF Query 中做的是获取多个列以将数据绑定绑定到列表视图。

我想要的列是 EnumerationCode、Part.Number、Part.Description。

我将使用 ProdID(我将 Products 绑定到 DropDown )作为这些附加值的获取器。

所以基本上我有这个想法 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 Parts where PartID = TernaryTable.PartID )

我认为对于如何获取这些数据以及如何保存对数据的任何更改可能有一个简单的解决方案 - 但正如我所说,我迷路了。

我需要 2 种方法,一种用于获取数据,另一种用于保存数据发生的任何更改。

我已经读过了

Get records from table and related related table using Linq to entity

Entity framework linq query Include() multiple children entities

但似乎我仍然迷失在多重关系中。

编辑

我认为在 Haralds 出色的回复之后,我需要并且可以发布更好的声明和真实的图表!

现在是表格信息:

我想要 MprnEnum.RefName、Part.PartNumber、Part.Name、Part.Description 其中 ProductID = [Product.ID - 我将提供的这个 ID] 由 MPRNENUM.RefName 订购。我希望能够使用 VS EF 从 DB 生成的实体类对 ProdPartEnum 执行 CRUD。

有 20 个 RefNames,每个 Product 有多个 EnumID,EnumID 可以属于多个 Products,每个 ProdEnums 都有一个关联的部分。 许多部分可以属于许多 ProdEnums

典型的表条目可能如下所示: ProdID = 1,EnumID = [1-20],PartID [1-1000] 当产品 ID = 1 时,我们有 20 行 EnumID,每一行都有一个 PartNumber。

如果需要,我可以发布更多详细信息。

【问题讨论】:

    标签: c# winforms entity-framework linq relationship


    【解决方案1】:

    天哪,这是一个奇怪的多对多对多!

    通常会有以下设计:

    • 每个Product 有零个或多个Parts,每个Part 用于零个或多个Products(多对多ProductPart
    • 每个Product 有零个或多个Enumerations,每个Enumeration 被零个或多个Products 使用(多对多ProductEnumeration
    • 每个Part 有零个或多个Enumerations,每个Enumeration 用于零个或多个Parts(多对多EnumerationPart

    这将导致三个联结表ProductPartProductEnumerationEnumerationPart

    您选择了一个只有一个接线表的设计。

    您确实意识到,在您的设计中,只要Product 具有PartProductPart 都具有相同的Enumeration,不是吗? product 不能比 productParts 更多或其他 EnumerationsProduct 不能有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,给我所有EnumerationCodesPart.NumberPart.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,
        });
    

    由您来编写包含四个表的连接。查看差异并决定从现在开始使用哪种方法。

    【讨论】:

    • 感谢您的完整回答,这对我有帮助。是的,这些关系很奇怪(也许我有一部分是错误的),因为每个 [Product / Part ] 都会有一个与之关联的枚举,即 ProductPart + Enum 。我不确定实体是否是使用导航属性创建的,我没有看到;它们看起来像装饰过的 POCO。我首先使用 DB 中的 VS EF 代码来生成它们。
    • 在您发表评论后,我用适当的图表更新了我的问题,并更好地阐明了模型。我认为更新是一个更清晰的描述。
    • 我喜欢导航属性——我的脑子里想的是 SQL。这得到了我想要的数据,我查看了 OrderBy 并且 (p => p.EnumerationCode) 有效。我认为我已经准备好了 - 我应该能够对数据进行更改并保存它 - 不确定调用保存更改时所有管道是如何实现的 - 但我希望它是直截了当的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多