介绍

评论本来是要放到标签里面去讲的,但是因为上一章东西有点多了,我就没放进去,这一章单独拿出来,内容不多大家自己写写就可以,也算是对前面讲解的一个小练习吧。

相关注释我也加在代码上面了,大家看看代码都可以理解。

评论仓储接口和实现

   public interface ICommentRepository : IBasicRepository<Comment, Guid>
{
/// <summary>
/// 根据文章Id 获取评论
/// </summary>
/// <param name="postId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<Comment>> GetListOfPostAsync(Guid postId, CancellationToken cancellationToken = default);
/// <summary>
/// 根据文章Id 获取评论数量
/// </summary>
/// <param name="postId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<int> GetCommentCountOfPostAsync(Guid postId, CancellationToken cancellationToken = default);
/// <summary>
/// 根据评论Id 下面的子获取评论
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<Comment>> GetRepliesOfComment(Guid id, CancellationToken cancellationToken = default); Task DeleteOfPost(Guid id, CancellationToken cancellationToken = default);
} public class EfCoreCommentRepository:EfCoreRepository<CoreDbContext, Comment, Guid>,ICommentRepository
{
public EfCoreCommentRepository(IDbContextProvider<CoreDbContext> dbContextProvider) : base(dbContextProvider)
{
} public async Task<List<Comment>> GetListOfPostAsync(Guid postId, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Where(a => a.PostId == postId)
.OrderBy(a => a.CreationTime)
.ToListAsync(GetCancellationToken(cancellationToken));
} public async Task<int> GetCommentCountOfPostAsync(Guid postId, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.CountAsync(a => a.PostId == postId, GetCancellationToken(cancellationToken));
} public async Task<List<Comment>> GetRepliesOfComment(Guid id, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Where(a => a.RepliedCommentId == id).ToListAsync(GetCancellationToken(cancellationToken));
} public async Task DeleteOfPost(Guid id, CancellationToken cancellationToken = default)
{
var recordsToDelete = await (await GetDbSetAsync()).Where(pt => pt.PostId == id).ToListAsync(GetCancellationToken(cancellationToken));
(await GetDbSetAsync()).RemoveRange(recordsToDelete);
}
}

评论接口和Dto

    public interface ICommentAppService : IApplicationService
{
Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId); Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input); Task<CommentWithDetailsDto> UpdateAsync(Guid id, UpdateCommentDto input); Task DeleteAsync(Guid id);
} public class CommentWithRepliesDto
{
public CommentWithDetailsDto Comment { get; set; } public List<CommentWithDetailsDto> Replies { get; set; } = new List<CommentWithDetailsDto>();
} public class CommentWithDetailsDto : FullAuditedEntityDto<Guid>
{
public Guid? RepliedCommentId { get; set; } public string Text { get; set; } public BlogUserDto Writer { get; set; }
} public class CreateCommentDto
{
public Guid? RepliedCommentId { get; set; } public Guid PostId { get; set; } [Required]
public string Text { get; set; }
} public class UpdateCommentDto
{
[Required]
public string Text { get; set; }
}

接口实现


public class CommentAppService : CoreAppService, ICommentAppService
{
private IUserLookupService<IdentityUser> UserLookupService { get; } private readonly ICommentRepository _commentRepository;
private readonly IGuidGenerator _guidGenerator; public CommentAppService(ICommentRepository commentRepository, IGuidGenerator guidGenerator, IUserLookupService<IdentityUser> userLookupService)
{
_commentRepository = commentRepository;
_guidGenerator = guidGenerator;
UserLookupService = userLookupService;
} public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId)
{
// 获取评论数据
var comments = await GetListOfPostAsync(postId); #region 对评论的作者进行赋值 var userDictionary = new Dictionary<Guid, BlogUserDto>(); foreach (var commentDto in comments)
{
if (commentDto.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(commentDto.CreatorId.Value); if (creatorUser != null && !userDictionary.ContainsKey(creatorUser.Id))
{
userDictionary.Add(creatorUser.Id, ObjectMapper.Map<IdentityUser, BlogUserDto>(creatorUser));
}
}
} foreach (var commentDto in comments)
{
if (commentDto.CreatorId.HasValue && userDictionary.ContainsKey((Guid)commentDto.CreatorId))
{
commentDto.Writer = userDictionary[(Guid)commentDto.CreatorId];
}
} #endregion var hierarchicalComments = new List<CommentWithRepliesDto>(); #region 包装评论数据格式 // 评论包装成2级(ps:前面的查询根据时间排序,这里不要担心子集在父级前面) foreach (var commentDto in comments)
{
var parent = hierarchicalComments.Find(c => c.Comment.Id == commentDto.RepliedCommentId); if (parent != null)
{
parent.Replies.Add(commentDto);
}
else
{
hierarchicalComments.Add(new CommentWithRepliesDto() { Comment = commentDto });
}
} hierarchicalComments = hierarchicalComments.OrderByDescending(c => c.Comment.CreationTime).ToList(); #endregion return hierarchicalComments; } public async Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input)
{
// 也可以使用这种方式(这里只是介绍用法) GuidGenerator.Create()
var comment = new Comment(_guidGenerator.Create(), input.PostId, input.RepliedCommentId, input.Text); comment = await _commentRepository.InsertAsync(comment); await CurrentUnitOfWork.SaveChangesAsync(); return ObjectMapper.Map<Comment, CommentWithDetailsDto>(comment);
} public async Task<CommentWithDetailsDto> UpdateAsync(Guid id, UpdateCommentDto input)
{
var comment = await _commentRepository.GetAsync(id); comment.SetText(input.Text); comment = await _commentRepository.UpdateAsync(comment); return ObjectMapper.Map<Comment, CommentWithDetailsDto>(comment);
} public async Task DeleteAsync(Guid id)
{
await _commentRepository.DeleteAsync(id); var replies = await _commentRepository.GetRepliesOfComment(id); foreach (var reply in replies)
{
await _commentRepository.DeleteAsync(reply.Id);
}
} private async Task<List<CommentWithDetailsDto>> GetListOfPostAsync(Guid postId)
{
var comments = await _commentRepository.GetListOfPostAsync(postId); return new List<CommentWithDetailsDto>(
ObjectMapper.Map<List<Comment>, List<CommentWithDetailsDto>>(comments));
} }

CreateMap<Comment, CommentWithDetailsDto>().Ignore(x => x.Writer);

结语

说明:

  • 1.整个评论的实现非常简单,我们只是实现了一个2层的嵌套。
  • 2.下一章我们讲授权和策略大家应该会比较喜欢,加油

联系作者:加群:867095512 @MrChuJiu

九、Abp vNext 基础篇丨评论聚合功能的更多相关文章

  1. 六、Abp vNext 基础篇丨文章聚合功能上

    介绍 9月开篇讲,前面几章群里已经有几个小伙伴跟着做了一遍了,遇到的问题和疑惑也都在群里反馈和解决好了,9月咱们保持保持更新.争取10月份更新完基础篇. 另外番外篇属于 我在abp群里和日常开发的问题 ...

  2. 七、Abp vNext 基础篇丨文章聚合功能下

    介绍 不好意思这篇文章应该早点更新的,这几天在忙CICD的东西没顾得上,等后面整好了CICD我也发2篇文章讲讲,咱们进入正题,这一章来补全剩下的 2个接口和将文章聚合进行完善. 开工 上一章大部分业务 ...

  3. 八、Abp vNext 基础篇丨标签聚合功能

    介绍 本章节先来把上一章漏掉的上传文件处理下,然后实现Tag功能. 上传文件 上传文件其实不含在任何一个聚合中,它属于一个独立的辅助性功能,先把抽象接口定义一下,在Bcvp.Blog.Core.App ...

  4. 十一、Abp vNext 基础篇丨测试

    前言 祝大家国庆快乐,本来想国庆之前更新完的,结果没写完,今天把剩下的代码补了一下总算ok了. 本章节也是我们后端日常开发中最重要的一步就是测试,我们经常听到的单元测试.集成测试.UI测试.系统测试, ...

  5. 五、Abp vNext 基础篇丨博客聚合功能

    介绍 业务篇章先从客户端开始写,另外补充一下我给项目起名的时候没多想起的太随意了,结果后面有些地方命名冲突了需要通过手动using不过问题不大. 开工 应用层 根据第三章分层架构里面讲到的现在我们模型 ...

  6. Abp vNext 基础篇丨分层架构

    介绍 本章节对 ABP 框架进行一个简单的介绍,摘自ABP官方,后面会在使用过程中对各个知识点进行细致的讲解. 领域驱动设计 领域驱动设计(简称:DDD)是一种针对复杂需求的软件开发方法.将软件实现与 ...

  7. Abp vNext 基础篇丨领域构建

    介绍 我们将通过例⼦介绍和解释⼀些显式规则.在实现领域驱动设计时,应该遵循这些规则并将其应⽤到解决⽅案中. 领域划分 首先我们先对比下Blog.Core和本次重构设计上的偏差,可以看到多了一个博客管理 ...

  8. 十、Abp vNext 基础篇丨权限

    介绍 本章节来把接口的权限加一下 权限配置和使用 官方地址:https://docs.abp.io/en/abp/latest/Authorization 下面这种代码可能我们日常开发都写过,ASP. ...

  9. 初识ABP vNext(11):聚合根、仓储、领域服务、应用服务、Blob存储

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 聚合根 仓储 领域服务 BLOB存储 应用服务 单元测试 模块引用 最后 前言 在前两节中介绍了ABP模块开发的基本步 ...

随机推荐

  1. 初识MySQL,关系型数据库&非关系型数据库

    初识MySQL,关系型数据库&非关系型数据库 数据库的分类: 关系型数据库:(SQL) MySQL,Oracle,Sql Server,DB2,SQLlite 通过表和表之间,行和列之间的关系 ...

  2. SQL--查询JSON、时间、字符串的高级用法

    SQL--查询JSON.时间.字符串的高级用法 本文章总结SQL的JSON.时间格式.字符串判断转换的使用.核心点还是在于Json字段的提取(1.5).时间的比较(2.2,2.3)以及字符串的查询(3 ...

  3. mapboxgl 互联网地图纠偏插件(三)

    先说结论,结论当然是:大功告成,喜大普奔.看效果图: 好了,接下来说一下过程 先回顾一下这个系列的第一篇和第二篇 第一篇是直接改的 mapboxgl 源码,在源码里面对瓦片的位置进行纠偏,遇到的问题是 ...

  4. CD管理和检索软件比较

    之前一直用EverCD+,考虑到鸡蛋不能放在一个篮子里,又找了几款功能类似的进行了比较,主要考察一下几个功能: 多个镜像:一个数据文件可以包含多个目录的镜像,便于数据管理和搜索: 目录更新:目录内容发 ...

  5. 英语学习Tips

    [edp] 写英语论文那些容易忽略的点: 连字符大小写问题 连字符"-"后首字母的大小写_周树森_新浪博客 (sina.com.cn) reference list 里面的书名要斜 ...

  6. Python对系统数据进行采集监控——psutil

    大家好,我是辰哥- 今天给大家介绍一个可以获取当前系统信息的库--psutil 利用psutil库可以获取系统的一些信息,如cpu,内存等使用率,从而可以查看当前系统的使用情况,实时采集这些信息可以达 ...

  7. ☕【Java技术指南】「Guava Collections」实战使用相关Guava不一般的集合框架

    Google Guava Collections 使用介绍 简介 Google Guava Collections 是一个对 Java Collections Framework 增强和扩展的一个开源 ...

  8. Linux下基于SQLite3 实现商店商品管理系统

    文章目录 一.SQLite相关C接口 重要接口 打开数据库 插入信息 查询 二.程序要求 三.程序说明 四.实现代码 水果店账单管理系统 一.SQLite相关C接口 如果第一次直接在命令行安装sqli ...

  9. MySQL 条件查询

    查询条件 having having的功能和where一样,都是用来筛选查询,不同的是,where在分组之前用,having必须在分组之后使用. # 查询每个部门大于30岁的平均工资,并且保留平均工资 ...

  10. Longhorn,企业级云原生容器分布式存储 - 监控(Prometheus+AlertManager+Grafana)

    内容来源于官方 Longhorn 1.1.2 英文技术手册. 系列 Longhorn 是什么? Longhorn 企业级云原生容器分布式存储解决方案设计架构和概念 Longhorn 企业级云原生容器分 ...