代码之家  ›  专栏  ›  技术社区  ›  vaindil

在EF核心中实现递归属性加载

  •  10
  • vaindil  · 技术社区  · 9 年前

    我正在使用.NET核心1.1.0、EF核心1.0和2015。

    我正在为帖子/评论编写一个系统,我需要一个函数来加载评论及其所有子元素及其相关属性。下面是我的类的简化版本:

    public class Comment
    {
        public long Id { get; set; }
    
        public string Content { get; set; }
    
        public User User { get; set; }
    
        public ICollection<Comment> Replies { get; set; }
    }
    
    public class User
    {
        public long Id { get; set; }
    
        public string Name { get; set; }
    
        public Avatar Avatar { get; set; }
    }
    
    public class Avatar
    {
        public string Url { get; set; }
    }
    

    任何给定评论可以有任意数量的回复:

    -PARENT
        -CHILD 1
        -CHILD 2
            -CHILD 3
        -CHILD 4
        -CHILD 5
            -CHILD 6
                -CHILD 7
    

    因此,给定父评论的ID,我需要加载整个树,包括用户和他们各自的化身。 (我在其他地方有控制,以确保这些树不会变得笨重,我不担心可能获取太多数据。)

    这个 Loading Related Data EF核心文档中的页面非常有用,但我不确定如何最好地处理这个问题。我尝试过把一些东西放在一起,但我只是无法概念化如何把它们放在一起。再次注意:我使用的是EF Core 1.1.0,因此我可以访问“显式加载”部分中的函数。

    如何在给定父注释ID的情况下加载整个注释树?

    3 回复  |  直到 9 年前
        1
  •  3
  •   CodingYoshi    9 年前

    我没有数据库,所以我只是在内存中做的,但如果你按照我的评论,它将为你工作。注意我内存中的对象,只有id为2的注释有回复。

    LoadComment 方法是一切发生的地方。剩下的只是我需要的设置代码。

    class Program
    {
        static void Main(string[] args)
        {
            var result = LoadComment(1, null);
            Console.ReadKey();
    
        }
    
    
    
    public static Comment LoadComment(long id, Comment com) 
    {
       Comment res = new Comment();
       if( com == null ) 
       {
          // You would call your context here and write db.Single(x => x.Id == id).Include(x => x.User.Avatar);
          var first = db.Single( x => x.Id == id );
    
          res = new Comment { Id = first.Id, Replies = first.Replies.ToList(), User = first.User };
          foreach( var item in first.Replies ) 
          {
             LoadComment( item.Id, item );
          }
       }
       else 
       {
          // You would call your context here and write db.Single(x => x.Id == id).Include(x => x.User.Avatar);
          var child = db.SingleOrDefault( x => x.Id == id );
          if( child == null ) 
          {
             return null;
          }
          com.Replies = new List<Comment>();
          com.Replies.Add( new Comment { Id = child.Id, Replies = child.Replies.ToList(), User = child.User } );
          foreach( var item in child.Replies ) 
          {
             LoadComment( item.Id, com );
          }
       }
    
    
       return res;
    }
    
        private static Comment cm1 = new Comment
        {
            Id = 1,
            User = new User { Id = 1, Avatar = new Avatar { Url = "1" } },
            Replies = new List<Comment> {
            new Comment { Id = 2 },
            new Comment { Id = 3 },
            new Comment { Id = 4 },
            new Comment { Id = 5 } },
            Content = "ContentForCommentId1"
        };
    
        private static Comment cm2 = new Comment
        {
            Id = 2,
            User = new User { Id = 2, Avatar = new Avatar { Url = "2" } },
            Replies = new List<Comment> {
            new Comment { Id = 22 },
            new Comment { Id = 33 },
            new Comment { Id = 44 },
            new Comment { Id = 55 } },
            Content = "ContentForCommentId2"
        };
        private static List<Comment> db = new List<Comment> { cm1, cm2 };
    
    }
    
        2
  •  1
  •   CountZero    7 年前

    我就是这样解决的。与Yoshi的非常相似,但可能对某人有帮助。

    private async Task<Comment> GetComment(Guid id, CancellationToken cancellationToken)
    {
        var wm = await _context.Comments
                               .Include(x => x.Replies)
                               .SingleOrDefaultAsync(x => x.Id == id, cancellationToken);
    
        for (var i = 0; i < wm.Replies.Count; i++)
        {
            if (!wm.Replies[i].IsDeleted)
                wm.Replies[i] = await GetComment(wm.Replies[i].Id, cancellationToken);
        }
    
        return wm;
    }
    
        3
  •  -1
  •   Elnaz    9 年前

    为什么你不使用热切加载? 急切地加载多个级别 我在.NETCore(VS2015)中的项目中使用了它

    还可以急切地加载多个级别的相关实体。下面的查询显示了如何对集合和引用导航属性执行此操作的示例。

    using (var context = new BloggingContext()) 
    { 
        // Load all blogs, all related posts, and all related comments 
        var blogs1 = context.Blogs 
                           .Include(b => b.Posts.Select(p => p.Comments)) 
                           .ToList(); 
    
        // Load all users their related profiles, and related avatar 
        var users1 = context.Users 
                            .Include(u => u.Profile.Avatar) 
                            .ToList(); 
    
        // Load all blogs, all related posts, and all related comments  
        // using a string to specify the relationships 
        var blogs2 = context.Blogs 
                           .Include("Posts.Comments") 
                           .ToList(); 
    
        // Load all users their related profiles, and related avatar  
        // using a string to specify the relationships 
        var users2 = context.Users 
                            .Include("Profile.Avatar") 
                            .ToList(); 
    }
    

    请注意,目前无法筛选加载了哪些相关实体。“包含”将始终包含所有相关实体。

    这是我的参考: "Microsoft.EntityFrameworkCore.SqlServer": "1.1.0-preview2-22683"

    我希望这对你有帮助。

    推荐文章