在asp core中使用sieve进行分页时,如何发送一个请求而不是两个请求?

问题描述 投票:0回答:1

我写这个查询的目的是为了让用户使用分页。我使用的是 Sieve 用于分页,过滤和排序,但我有一个问题,这个查询。

我打算发送一个单一的请求到数据库,并只是返回我这个数据。

 public class UserPagingDto
{
    [Sieve(CanFilter = true, CanSort = true)]
    public Guid Id { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public string Username { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public string DisplayName { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public bool IsActive { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public bool IsLockedEnd { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public bool ConfirmPhoneNumber { get; set; }
    public UserPagingInfo UserInfos { get; set;
    }
}

public class UserPagingInfo
{
    [Sieve(CanFilter = true, CanSort = true)]
    public int AccountFaile { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public bool ConfirmEmail { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public string PhoneNumber { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public DateTimeOffset? LockedEnd { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public string? Email { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public string Name { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public string Family { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public string RoleName { get; set; }
    [Sieve(CanFilter = true, CanSort = true)]
    public Guid RoleId { get; set; }

}

为了这个目的,我写了这个。

    var user = Users.AsNoTracking().Select(x => new
            {
                Id=x.Id,
                Username = x.Username,
                Name = x.Name,
                Family = x.Family,
                DisplayName = $"{x.Name} {x.Family}",
                Email = x.Email,
                PhoneNumber = x.PhoneNumber,
                AccountFaile = x.AccountFaile,
                IsActive = x.IsActive,
                IsLockedEnd = x.IsLockedEnd,
                ConfirmPhoneNumber = x.ConfirmPhoneNumber,
                ConfirmEmail = x.ConfirmEmail,
                LockedEnd = x.LockedEnd,
                Role = x.UserRoles.Role
            }).Select(c => new UserPagingDto
            {
                Id=c.Id,
                ConfirmPhoneNumber = c.ConfirmPhoneNumber,
                DisplayName = c.DisplayName,
                IsActive = c.IsActive,
                IsLockedEnd = c.IsLockedEnd,
                Username = c.Username,
                UserInfos = new UserPagingInfo
                {
                    AccountFaile = c.AccountFaile,
                    ConfirmEmail = c.ConfirmEmail,
                    Email = c.Email,
                    Family = c.Family,
                    LockedEnd = c.LockedEnd,
                    Name = c.Name,
                    PhoneNumber = c.PhoneNumber,
                    RoleId = c.Role.Id,
                    RoleName = c.Role.Name
                }
            });
            var sieveModel = new SieveModel
            {
                PageSize = formQuery.PageSize,
                Filters = formQuery.Filters,
                Page = formQuery.Page,
                Sorts = formQuery.Sorts
            };
            var result = sieveProcessor.Apply(sieveModel, user);
            return OperationResult<GetAllPaging<UserPagingDto>>.BuildSuccessResult(new GetAllPaging<UserPagingDto>
            {
                Records = result,
                TotalCount = await Users.CountAsync()
            });

但它发送的是 两种 请求到数据库,但效果不好。我想通过发送一个请求来达到同样的结果。

我用profiler跟踪对数据库的查询。

一个查询是这个。

    SELECT COUNT(*)
    FROM [User] AS [u]
         WHERE [u].[IsDelete] = CAST(0 AS bit)

第二个查询是这个,我打算只发送这个查询。

 SELECT [u].[Id], [u].[ConfirmPhoneNumber], [u].[Name], [u].[Family], [u].[IsActive], [u].[IsLockedEnd], [u].[Username], [u].[AccountFaile], [u].[ConfirmEmail], [u].[Email], [u].[LockedEnd], [u].[PhoneNumber], [t0].[Id], [t0].[Name]
FROM [User] AS [u]
LEFT JOIN (
    SELECT [u0].[Id], [u0].[IsDelete], [u0].[RoleId], [u0].[UserId]
    FROM [UserRole] AS [u0]
    WHERE [u0].[IsDelete] = CAST(0 AS bit)
) AS [t] ON [u].[Id] = [t].[UserId]
LEFT JOIN (
    SELECT [r].[Id], [r].[Description], [r].[IsDelete], [r].[Name], [r].[SecurityStamp]
    FROM [Role] AS [r]
    WHERE [r].[IsDelete] = CAST(0 AS bit)
) AS [t0] ON [t].[RoleId] = [t0].[Id]
WHERE [u].[IsDelete] = CAST(0 AS bit)
ORDER BY [u].[Id]
OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY
c# entity-framework entity-framework-core sieve
1个回答
2
投票

你混淆了查询和集合。

这个。

var user = Users.AsNoTracking().Select(x => new
            {
   . . .
                    RoleId = c.Role.Id,
                    RoleName = c.Role.Name
                }
            });

不是一个对象的集合 它是一个查询。 如果你把查询传递给一个方法,这个方法运行的是 .Count() 然后 .ToList() 你正在运行两个不同的查询。

所以只需运行一次查询,并将结果存储在List中。

var userQuery = Users.AsNoTracking().Select(x => new
            {
   . . .
                    RoleId = c.Role.Id,
                    RoleName = c.Role.Name
                }
            });
 var user = userQuery.ToList();

1
投票

让我们来思考一下这个问题。你有一个表,我们假设有100 000 000条记录。在过滤和应用你的分页设置后,你的查询最终会得到一页记录,让我们假设有100条这样的项目。从项数(例子中是100项)你无法推断出总数(例子中是100 000 000)。如果我们看一下这些字段,没有一个字段给出任何关于总记录数的提示。增加一列所有记录的计数是一个非常糟糕的主意。

所以,如果我们保持理智,那么我们承认,你打算作为唯一的查询的查询将不会给你任何关于计数的信息。为了补救这种情况,你需要将该计数存储为一个新的记录。服务器变量. 如果你以这样的方式缓存了计数,那么你将能够使用该服务器变量,并随时重复使用它,而不是在每次加载时硬性计算它。然而,为了保证这个工作的顺利进行,你将需要维护这个值。

  • 每当有这样的记录被插入时,就增加服务器上的计数值。
  • 每当有这样的记录被删除,就在服务器上递减计数值。
  • 每当这样的记录被取消删除时,就在服务器上增加计数值。
  • 当然,如果发生批量插入、删除或取消删除的情况,则需要相应地改变计数
  • 你还需要一个心跳任务来确保你的服务器变量与实际计数同步,因为记录也可以在你的应用程序之外被创建、删除和更新,至少是通过在数据库上直接执行查询来实现的
© www.soinside.com 2019 - 2024. All rights reserved.