EF Core - 在不加载实体的情况下修改实现

时间:2021-04-29 09:09:41

标签: c# entity-framework entity-framework-core

我试图实现一个让用户喜欢评论的功能。如果用户已经喜欢它,则不能再次喜欢它,反之亦然。 这是它的样子:

    public async Task<ActionResult<CommentResponse>> LikeComment(LikeComment like)
    {
        if (like.HasNullProperty())
            return BadRequest("Missing properties!");
        var comment = await commentService.GetCommentWithLikes((int) like.CommentId);
        if(comment is null)
            return NotFound($"No comment with id {like.CommentId} was found");
        try
        {
            var userId = User.GetUserID();
            comment = await commentService.LikeComment(comment, userId, (bool)like.Liked);
            return comment is not null ? Ok(comment.GetCommentResponse((bool)like.Liked)) : StatusCode(304);
        }
        catch(Exception e)
        {
            return StatusCode(500, $"Error while trying to {((bool)like.Liked ? "like" : "dislike")} comment");
        }
    }

相关功能:

    public async Task<Comment> GetCommentWithLikes(int id) => await blogContext.Comments.IncludeLikes().FirstOrDefaultAsync(x => x.Id == id);
    public static IQueryable<Comment> IncludeLikes(this IQueryable<Comment> source)
        => source.Select(x => new Comment
        {
            Id = x.Id,
            ArticleId = x.ArticleId,
            CreatedById = x.CreatedById,
            CreatedAt = x.CreatedAt, 
            Likes = x.LikedBy.Count,
            Text = x.Text,
        });

还有主要的逻辑:

    public async Task<Comment> LikeComment(Comment comment, string userId, bool liked)
    {
        var user = new User { Id = userId };
        var hasLiked = await blogContext.Comments.Where(x => x.Id == comment.Id && x.LikedBy.Any(x => x.Id == user.Id)).FirstOrDefaultAsync() is not null;
        Action action = null;
        if (!hasLiked && liked)
        {
            action = () => comment.LikedBy.Add(user);
            comment.LikedBy = new List<User>();
            comment.Likes++;
        }
        else if (hasLiked && !liked)
        {
            action = () => comment.LikedBy.Remove(user);
            comment.LikedBy = new List<User> { user };
            comment.Likes--;
        }
        if (action is null)
            return null;
        blogContext.Attach(user);
        blogContext.Attach(comment);
        action();
        await blogContext.SaveChangesAsync();
        return comment;
    }

我们的想法是不加载整个 likeBy 关系,但仍通知 EF Core 我已添加或删除了一个用户。因此,我修改了评论,然后附加它,以便 EF Core 跟踪对 likeBy 关系的更改。有趣的是,它在喜欢评论时工作正常。但是,当不喜欢时,我收到一条错误消息,表明该评论已附加。在 GetCommentsWithLikes 函数中使用 .AsNoTracking() 没有帮助。

<块引用>

无法跟踪实体类型“Comment”的实例,因为已经跟踪了另一个具有相同 {'Id'} 键值的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”来查看冲突的键值。

这是链接时传递给 like func 的注释(有效):enter image description here

这是不喜欢时的那个(只有差异是喜欢计数......):enter image description here

这是在失败附加之前:enter image description here

也许有人知道这种行为的原因,可以帮助我或建议不同的方法:) 谢谢

1 个答案:

答案 0 :(得分:2)

<块引用>

在 GetCommentsWithLikes 函数中使用 .AsNoTracking() 没有帮助

由于使用了投影,该函数已经隐式没有跟踪。这是下面的电话

var hasLiked = await blogContext.Comments
    .Where(x => x.Id == comment.Id && x.LikedBy.Any(x => x.Id == user.Id))
    .FirstOrDefaultAsync() is not null;

当结果不为空时,它会向更改跟踪器添加一个 Comment 实例。

由于您不需要该实例并且只是检查是否存在,请改用以下不涉及实体实例的纯服务器端查询:

var hasLiked = await blogContext.Comments
    .AnyAsync(x => x.Id == comment.Id && x.LikedBy.Any(x => x.Id == user.Id));