【问题标题】:EF Core 6 Database First Parent Child Relation issueEF Core 6 数据库第一父子关系问题
【发布时间】:2022-11-02 20:35:58
【问题描述】:

我们正在使用 .NET 6 和 EF Core 6 以及现有的 SQL Server 数据库构建应用程序。我们使用数据库优先方法并运行 Scaffold-DbContext 工具,我们能够生成 dbcontex 类。一切正常,两个表之间的父子关系的一部分:

脚手架工具,为上面的表格生成了以下两个类:

public partial class TreeNode
    {
        public TreeNode()
        {
            TreeNodeHierarchyChildren = new HashSet<TreeNodeHierarchy>();
            TreeNodeHierarchyParents = new HashSet<TreeNodeHierarchy>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Code { get; set; }
        public bool IsLeaf { get; set; }
        public int? OrganisationId { get; set; }
        public bool IsDeleted { get; set; }

        public virtual ICollection<TreeNodeHierarchy> TreeNodeHierarchyChildren { get; set; }
        public virtual ICollection<TreeNodeHierarchy> TreeNodeHierarchyParents { get; set; }
    }


public partial class TreeNodeHierarchy
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public int ChildId { get; set; }

      
        public virtual TreeNode Child { get; set; }

        public virtual TreeNode Parent { get; set; }
    }

并在 dbcontext 类中进行以下映射:

modelBuilder.Entity<TreeNode>(entity =>
            {
                entity.ToTable("TreeNode");
                entity.Property(e => e.Code).HasMaxLength(100);
                entity.Property(e => e.Name)
                    .IsRequired()
                    .HasMaxLength(255);
            });

            modelBuilder.Entity<TreeNodeHierarchy>(entity =>
            {
                entity.ToTable("TreeNodeHierarchy");

                entity.HasOne(d => d.Child)
                    .WithMany(p => p.TreeNodeHierarchyChildren)
                    .HasForeignKey(d => d.ChildId)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("FK_TreeNodeHierarchy_TreeNode_Child");

                entity.HasOne(d => d.Parent)
                    .WithMany(p => p.TreeNodeHierarchyParents)
                    .HasForeignKey(d => d.ParentId)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("FK_TreeNodeHierarchy_TreeNode_Parent");
            });

这是问题,当我写以下内容时:

var nodes = _context.TreeNodes.Include(th => th.TreeNodeHierarchyChildren)
                .Where(tn => tn.IsLeaf)
                .....

它加载孩子而不是父母。

此关系在使用 LINQ to SQL 的当前应用程序 (.net 4.7) 中正常工作。

我错过了什么吗?

更新

正如@SpruceMoose 所建议的那样,我还在查询中包含了 TreeNodeHierarchyParents 属性,但它没有解决问题。

var nodes = _context.TreeNodes
            .Include(th => th.TreeNodeHierarchyChildren)
            .Include(th => th.TreeNodeHierarchyParents)
            .Where(tn => tn.IsLeaf)

更新#2

我应用了@Dave 建议的映射,我认为这很有意义(最后,关系就像 Windows 文件夹/文件系统)。 无论如何,仍然有一些东西不能正常工作。当我调试以下代码时:

var nodes = _context.TreeNodes
    .Include(th => th.TreeNodeHierarchyChildren)
    .Include(th => th.TreeNodeHierarchyParents)
    .Where(tn => tn.IsLeaf)
    .ToList();   

我仍然看到父级尚未加载

【问题讨论】:

    标签: c# sql-server .net-core entity-framework-core ef-database-first


    【解决方案1】:

    您需要通过在TreeNodeHierarchyParents 导航属性上使用Include() 显式(急切地)加载Parent 元素(就像您当前使用TreeNodeHierarchyChildren 导航属性一样)。

    将您的 linq 查询更改为以下内容:

    var nodes = _context.TreeNodes
                .Include(th => th.TreeNodeHierarchyChildren)
                .Include(th => th.TreeNodeHierarchyParents)
                .Where(tn => tn.IsLeaf)
                .....
    

    【讨论】:

      【解决方案2】:

      我认为您的关系映射是错误的。你说一个孩子有很多孩子,一个父母有很多父母。应该是一个孩子有很多父母,一个父母有很多孩子。

      我认为在双方都定义这些类型的关系也是一个好主意,这样如果你出错了,它会更快地显示为错误。另请注意,我认为其中一些陈述已经是默认的。

      另外,重要的是,请注意我认为您需要使用可空引用类型来指示可空性。任何应该可以为空的东西都应该在实体类型中的类型名称上具有?。尽管我认为您可能应该级联删除,而不是设置为空。这取决于您的模型如何工作。

      像这样的东西,虽然我不能保证编译:

      modelBuilder.Entity<TreeNode>(tnb => {
         tnb.ToTable("TreeNode");
         tnb.Property(tn => tn.Code).HasMaxLength(100);
         tnb.Property(tn => tn.Name).IsRequired().HasMaxLength(255);
      
         tnb
         .HasMany(tn => tn.TreeNodeHierarchyParents)
         .WithOne(tnh => tnh.Child);
      
         tnb
         .HasMany(tn => tn.TreeNodeHierarchyChildren)
         .WithOne(tnh => tnh.Parent);   
      });
      
      modelBuilder.Entity<TreeNodeHierarchy>(tnhb => {
         tnhb.ToTable("TreeNodeHierarchy");
      
         tnhb
         .HasOne(tnh => tnh.Child)
         .WithMany(tn => tn.TreeNodeHierarchyParents)
         .HasForeignKey(tnh => tnh.ChildId)
         .OnDelete(DeleteBehavior.ClientSetNull)
         .HasConstraintName("FK_TreeNodeHierarchy_TreeNode_Child");
      
         tnhb
         .HasOne(tnh => tnh.Parent)
         .WithMany(tn => tn.TreeNodeHierarchyChildren)
         .HasForeignKey(tnh => tnh.ParentId)
         .OnDelete(DeleteBehavior.ClientSetNull)
         .HasConstraintName("FK_TreeNodeHierarchy_TreeNode_Parent");
      });
      

      为了确保模型定义正确,您可以做的一件事是使用它创建一个空的第二个数据库,并将其模型与真实模型进行比较,然后继续对其进行微调,直到正确为止。

      【讨论】:

      • 我用你的建议更新了我的问题,请看看结果。
      【解决方案3】:

      关于更新#2:

      您急切地加载了 TreeNodeHierarchy 类型的关系实体,但您并没有急切地加载它们引用的 TreeNode 实体。您需要添加 .ThenInclude 调用来执行此操作。

      var nodes = _context.TreeNodes
                  .Include(th => th.TreeNodeHierarchyChildren)
                      .ThenInclude(tnhc => tnhc.Child)
                  .Include(th => th.TreeNodeHierarchyParents)
                      .ThenInclude(tnhp => tnhp.Parent)
                  .Where(tn => tn.IsLeaf)
      

      您的示例中的 Child 属性已填充到您的当前查询中,因为 TreeNode 实体由您的基本查询加载,并且 EF Core 会自动将其连接到其他跟踪实体中的相关导航属性。因此,如果没有附加的 ThenInclude,则不会加载任何不是 IsLeaf 的“父级”(也不是任何不是 IsLeaf 的“子级”)。

      另一种(不推荐)替代方法是启用延迟加载。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多