Как отправить один запрос вместо двух при использовании sieve в ядре asp для подкачки? - PullRequest
1 голос
/ 01 мая 2020

Я пишу этот запрос с целью привлечения пользователей с подкачкой страниц. Я использую 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()
            });

, но он отправляет два запроса в базу данных, и это не хорошо. Я хотел бы добиться того же результата, отправив один запрос.

Я отслеживаю запросы к базе данных с помощью профилировщика.

один запрос такой:

    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

Ответы [ 2 ]

2 голосов
/ 01 мая 2020

Вы путаете запросы и коллекции.

Это:

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

Не является коллекцией объектов. Это запрос. Если вы передаете запрос методу, который запускает .Count(), а затем .ToList(), вы запускаете два разных запроса.

Так что просто запустите запрос один раз и сохраните результаты в списке. Например:

var userQuery = Users.AsNoTracking().Select(x => new
            {
   . . .
                    RoleId = c.Role.Id,
                    RoleName = c.Role.Name
                }
            });
 var user = userQuery.ToList();
1 голос
/ 01 мая 2020

Давайте немного подумаем об этом. У вас есть таблица, давайте предположим, что есть 100 000 000 записей. После фильтрации и применения настроек подкачки ваш запрос заканчивается страницей записей. Предположим, что существует 100 таких элементов. Из числа элементов (в данном примере это 100) вы не можете определить общее количество (в данном примере это 100 000 000). Если мы посмотрим на поля, ни один из них не дает никаких подсказок об общем количестве записей. Добавление столбца для подсчета для всех записей было бы крайне плохой идеей.

Таким образом, если мы останемся разумными, то мы признаем, что запрос, который вы намереваетесь стать единственным запросом, не даст вам никакой информации о счет. Чтобы исправить эту ситуацию, вам нужно сохранить этот счет как серверную переменную . Если у вас счетчик кешируется таким образом, вы сможете использовать эту серверную переменную и использовать ее всякий раз, когда захотите, вместо жесткого вычисления при каждой загрузке. Однако, чтобы убедиться, что это работает хорошо, вам нужно будет поддерживать это значение:

  • всякий раз, когда такая запись вставляется, увеличивайте значение счетчика на сервере
  • всякий раз, когда такое запись удаляется, уменьшая значение счетчика на сервере
  • всякий раз, когда такая запись восстанавливается, конечно же увеличивайте значение счетчика на сервере
  • , если происходит пакетная вставка, удаление или восстановление, тогда вам нужно будет соответственно изменить счет
  • вам также понадобится задание пульса, чтобы убедиться, что ваша переменная сервера синхронизирована с фактическим счетом, поскольку записи могут создаваться / удаляться / обновляться вне вашего приложения. а также, по крайней мере, с помощью прямых запросов к базе данных
...