ABP框架IRepository分页与计数不一致问题求助(含软删除关联)
问题:ABP框架下PagedResultDto总数与返回条目数不一致
你编写的ABP框架代码用于获取关联Tenant的Experience全部记录,但出现totalCount与返回items数量不匹配的问题:当MaxResultCount设为10时,总计数显示16但仅返回4条;设为100时总计数仍为16但返回10条。排查发现部分关联的Tenant已通过IHasDeletionTime软删除,这些记录被计入总数但未出现在结果中。
原代码
public async Task<PagedResultDto<GetExperienceForGuestDto>> GetSearchExperienceForGuest( GetExperiencesForGuestInput input) { return await _unitOfWorkManager.WithUnitOfWorkAsync(async () => { using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MustHaveTenant)) { var filteredExperiences = _experienceRepository.GetAll() .Include(x => x.Tenant) .Include(x => x.Calendars) .Where(e => e.Status == ExperienceStatus.Approve) .WhereIf(!string.IsNullOrWhiteSpace(input.SearchTitle), e => e.Title.Contains(input.SearchTitle) || e.Description.Contains(input.SearchTitle)) .WhereIf(input.BookingDate != default, e => e.Calendars.Any(c => c.Date.Date == input.BookingDate.Date && c.Type != Calendar.Enums.CalendarType.Reject)) .WhereIf(input.TenantId != null, e => e.TenantId == input.TenantId) .OrderBy(input.Sorting ?? "id asc"); var query = filteredExperiences.PageBy(input); var dbList = await query.ToListAsync(); var results = new List<GetExperienceForGuestDto>(); foreach (var o in dbList) { var res = new GetExperienceForGuestDto() { Title = o.Title, Description = StringHelper.TruncateAndStripHtml(o.Description), Tenant = ObjectMapper.Map<ExperienceTenantDto>(o.Tenant), Thumbnail = _webUrlService.GetUploadedImageUrl(o.Thumbnail), Id = o.Id, CreationTime = o.CreationTime }; results.Add(res); } return new PagedResultDto<GetExperienceForGuestDto>( await filteredExperiences.CountAsync(), results ); } }); }
问题原因
filteredExperiences.CountAsync()仅统计了符合自身过滤条件的Experience记录,但未考虑关联Tenant的软删除状态;- 执行
query.ToListAsync()时,EF Core结合ABP的软删除过滤器(针对ISoftDelete接口),会自动排除关联Tenant已被软删除的Experience记录,导致实际返回的条目数少于统计的总数。
修复方案
在filteredExperiences的查询中显式添加Tenant未被软删除的过滤条件,确保Count和ToList使用完全一致的过滤规则:
public async Task<PagedResultDto<GetExperienceForGuestDto>> GetSearchExperienceForGuest( GetExperiencesForGuestInput input) { return await _unitOfWorkManager.WithUnitOfWorkAsync(async () => { using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MustHaveTenant)) { var filteredExperiences = _experienceRepository.GetAll() .Include(x => x.Tenant) .Include(x => x.Calendars) // 新增:过滤关联Tenant未被软删除的记录 .Where(e => !e.Tenant.IsDeleted) .Where(e => e.Status == ExperienceStatus.Approve) .WhereIf(!string.IsNullOrWhiteSpace(input.SearchTitle), e => e.Title.Contains(input.SearchTitle) || e.Description.Contains(input.SearchTitle)) .WhereIf(input.BookingDate != default, e => e.Calendars.Any(c => c.Date.Date == input.BookingDate.Date && c.Type != Calendar.Enums.CalendarType.Reject)) .WhereIf(input.TenantId != null, e => e.TenantId == input.TenantId) .OrderBy(input.Sorting ?? "id asc"); var query = filteredExperiences.PageBy(input); var dbList = await query.ToListAsync(); var results = dbList.Select(o => new GetExperienceForGuestDto() { Title = o.Title, Description = StringHelper.TruncateAndStripHtml(o.Description), Tenant = ObjectMapper.Map<ExperienceTenantDto>(o.Tenant), Thumbnail = _webUrlService.GetUploadedImageUrl(o.Thumbnail), Id = o.Id, CreationTime = o.CreationTime }).ToList(); return new PagedResultDto<GetExperienceForGuestDto>( await filteredExperiences.CountAsync(), results ); } }); }
补充说明
- 假设你的Tenant实体实现了ABP的
ISoftDelete接口,包含IsDeleted布尔属性; - 将原来的foreach循环改成
Select链式调用,简化代码逻辑,不影响功能; - 修复后,
filteredExperiences.CountAsync()统计的总数会自动排除关联Tenant已删除的记录,与实际返回的条目数保持一致。
内容的提问来源于stack exchange,提问作者Dương Việt Hoàng
相关产品推荐
相关产品推荐

