在EF Core Linq查询中连接表格

5
我目前正在尝试使用EF Core创建Web API,并且在连接表格时遇到了一些问题。我正在使用以下数据库图表进行操作: enter image description here 目前从API获取的数据如下:
[  
   {  
      "postId":1,
      "postDate":"2018-10-21T21:56:43.9838536",
      "content":"First entry in posts!",
      "user":{  
         "userId":1,
         "creationDate":"2018-10-21T21:56:36.3539549",
         "name":"Hansel"
      },
      "comments":[  
         {  
            "commentId":1,
            "postDate":"0001-01-01T00:00:00",
            "content":"Nice!",
            "user":null
         },
         {  
            "commentId":2,
            "postDate":"0001-01-01T00:00:00",
            "content":"Cool, here's another comment",
            "user":null
         },
         {  
            "commentId":3,
            "postDate":"0001-01-01T00:00:00",
            "content":"and the last one for the night",
            "user":null
         }
      ]
   },
   {  
      "postId":2,
      "postDate":"2018-10-22T21:56:44.0650102",
      "content":"Its good to see that its working!",
      "user":{  
         "userId":2,
         "creationDate":"2018-10-16T21:56:36.4585213",
         "name":"Chris"
      },
      "comments":[  

      ]
   }
]

正如你所看到的,它几乎可以工作了,我的问题在于评论中的空值,我希望能够把用户也取出来,但是出现了某些问题,我不能做到。

我的当前查询语句如下(我正在使用DTO来清理生成的JSON):

var result = from post in context.Posts
                join user in context.Users 
                on post.User.UserId equals user.UserId
                join comment in context.Comments                            
                on post.PostId equals comment.Post.PostId into comments
                select new PostDTO
                {
                    Content = post.Content,
                    PostDate = post.PostDate,
                    User = UserDTO.UserToDTO(user),
                    Comments = CommentDTO.CommentToDTO(comments.ToList()),
                    PostId = post.PostId
                };

如果我使用SQL,我会将用户加入“评论”,但我不能这样做,所以我尝试了一个类似的解决方案,我认为这样可以解决问题。
var result = from post in context.Posts
                join user in context.Users on post.User.UserId equals user.UserId
                join comment in 
                    (from u in context.Users
                    join c in context.Comments
                    on u.UserId equals c.User.UserId select c)
                on post.PostId equals comment.Post.PostId into comments
                select new PostDTO
                {
                    Content = post.Content,
                    PostDate = post.PostDate,
                    User = UserDTO.UserToDTO(user),
                    Comments = CommentDTO.CommentToDTO(comments.ToList()),
                    PostId = post.PostId
                };

然而,虽然这个查询确实执行了,但结果与我写的第一个查询相同,问题在于用户没有加入评论。
简而言之,我可以将用户连接到帖子和评论连接到帖子,但我无法将用户绑定到评论。
希望您能帮助我,提前感谢:)
编辑:这是我的模型。
public class Comment
{
    public int CommentId { get; set; }
    public DateTime PostDate { get; set; }
    public string Content { get; set; }

    public User User { get; set; }

    public Post Post { get; set; }
}

  public class User
    {
        public int UserId { get; set; }
        public DateTime CreationDate { get; set; }
        public string Name{ get; set; }

        public ICollection<Post> Posts { get; set; }
        public ICollection<Comment> Comments { get; set; }
    }

public class Post
{
    public int PostId { get; set; }
    public DateTime PostDate { get; set; }
    public string Content { get; set; }
    public User User { get; set; }

    public ICollection<Comment> Comments { get; set; }
}

1
不要使用 join,而是使用导航属性。并且使用 AutoMapper,这样你就可以在一行代码中将实体类投影到 DTO 类。当前的代码会触发客户端评估,因为 CommentDTO.CommentToDTO() 无法被翻译成 SQL。 - Gert Arnold
你能发布模型吗?正如@Gert所提到的,你应该真正使用导航属性。奇怪的是,你似乎已经有了它们,但是用它们来连接。但是这里的user变量join user in context.Users on post.User.UserId equals user.UserIdpost.User相同。如果我们继续下去,post应该有ICollecton<Comment> CommentsComment应该有User User,所以你应该只需要使用select(投影)就可以得出结果。 - Ivan Stoev
我现在已经发布了模型。我有导航属性,但是我不确定如何正确地利用它们,因为我没有找到与我的数据库结构类似的示例。我尝试使用仅包含查询,但它给了我太多数据,例如:帖子+评论+评论用户+用户的评论和用户的帖子。 - jAndersen
@GertArnold 我看到了很多人建议使用AutoMapper,如果我是在做职业工作的话,我可能会使用它,因为它比手动操作更快。但是我现在只是出于兴趣和想要学习这些东西而进行操作 :) 无论如何还是感谢你的建议! - jAndersen
1
AutoMapper也很有趣 :) 但我明白你的意思。愉快编程! - Gert Arnold
1个回答

7
要获取带有评论(以及每条评论的用户)和帖子用户的帖子,只需使用.Include()即可。
var res = this._dbContext.Posts
        .Include(p => p.User)
        .Include(p => p.Comments)
            .ThenInclude(c => c.User)
        .ToList();

enter image description here

如果你不想使用第三方库,比如 AutoMapper,你可以创建一个 convertPostToDto 函数并使用 .

res.Select(p => convertPostToDto(p))

res 转换为结果

如果您不喜欢引入第三方库,比如 AutoMapper,则可以创建一个名为 convertPostToDto 的函数并进行转换。

天啊,我甚至没有想过用那种方式去做,现在感觉自己非常愚蠢。我会尝试一下看看是否适用于我 :) - jAndersen
1
除了最后一个建议外,一切都很好。不要创建函数并像建议的那样使用它 - 这会导致客户端评估,应该避免这种情况。 - Ivan Stoev
1
@IvanStoev 非常感谢。我没有意识到可以在数据库中将帖子转换为DTO。稍后我会尝试,如果我能确认所有的转换都可以在数据库中完成,我会更新我的答案。 - itminus
1
其实并不是在数据库中,而是在将查询结果实例化时(它作为 DbDataReader 返回并需要转换为对象)。但是使用可发现的投影允许 EF 创建必要的 SQL SELECT 并避免创建(和跟踪)中间实体对象。基本上这就是 AutoMapper 的 MapProjectTo 之间的区别 :) 不管怎样,你很受欢迎。 - Ivan Stoev
我正在编写一个联接查询,然后看到了.Include()。救星 :) @IvanStoev - Syed Rafay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接