You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 10:52:03