分页排序和多条件查询

This commit is contained in:
hello 2024-04-09 22:56:19 +08:00
parent 62a8b7f569
commit 6fef1a9ea0
7 changed files with 161 additions and 16 deletions

View File

@ -8,7 +8,7 @@ using HelloShop.ServiceDefaults.Models.Paging;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections;
using HelloShop.ServiceDefaults.Extensions;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
@ -22,13 +22,21 @@ namespace HelloShop.IdentityService.Controllers
[Authorize(IdentityPermissions.Users.Default)]
public async Task<ActionResult<PagedResponse<UserListItem>>> GetUsers([FromQuery] UserListRequest model)
{
var userList = await dbContext.Set<User>().Skip((model.PageNumber - 1) * model.PageSize).Take(model.PageSize).ToListAsync();
IQueryable<User> users = dbContext.Set<User>();
var result = mapper.Map<IReadOnlyList<UserListItem>>(userList);
if (model.Keyword is not null)
{
users = users.Where(e => e.UserName != null && e.UserName.Contains(model.Keyword));
}
var responseModel = new PagedResponse<UserListItem>(result, result.Count);
users = users.WhereIf(model.PhoneNumber is not null, e => e.PhoneNumber == model.PhoneNumber);
return Ok(responseModel);
IQueryable<User> pagedUsers = users.SortAndPageBy(model);
List<User> pagedUserList = await pagedUsers.ToListAsync();
int totalCount = await users.CountAsync();
return new PagedResponse<UserListItem>(mapper.Map<List<UserListItem>>(pagedUserList), totalCount);
}
[HttpGet("{id}")]

View File

@ -53,6 +53,19 @@ namespace HelloShop.IdentityService.DataSeeding
}
await userManager.AddToRoleAsync(guestUser, "GuestRole");
if (userManager.Users.Count() < 30)
{
for (int i = 0; i < 30; i++)
{
var user = new User
{
UserName = $"user{i}",
Email = $"test{i}@test.com",
};
await userManager.CreateAsync(user, user.UserName);
}
}
}
}
}

View File

@ -0,0 +1,78 @@
using HelloShop.ServiceDefaults.Constants;
using HelloShop.ServiceDefaults.Models.Paging;
using System.Linq.Expressions;
using System.Reflection;
namespace HelloShop.ServiceDefaults.Extensions;
public static class QueryableExtensions
{
public static IQueryable<TEntity> SortBy<TEntity>(this IQueryable<TEntity> query, string? orderBy = null)
{
PropertyInfo[] properties = typeof(TEntity).GetProperties();
IOrderedQueryable<TEntity>? orderedQueryable = null;
if (!string.IsNullOrWhiteSpace(orderBy))
{
// Convert expressions of the form field1 desc,field2 asc
string[] orderBySubs = orderBy.Split(',');
foreach (var orderBySub in orderBySubs)
{
string[] orderByParts = orderBySub.Trim().Split(' ');
if (orderByParts.Length >= 1)
{
string propertyName = PascalCaseNamingPolicy.PascalCase.ConvertName(orderByParts[0]);
bool ascending = orderByParts.Length == 1 || (orderByParts.Length == 2 && orderByParts[1].Equals("asc", StringComparison.OrdinalIgnoreCase));
if (properties.Any(x => x.Name == propertyName))
{
if (ascending)
{
orderedQueryable = orderedQueryable is null ? query.OrderBy(propertyName) : orderedQueryable.ThenBy(propertyName);
}
else
{
orderedQueryable = orderedQueryable is null ? query.OrderByDescending(propertyName) : orderedQueryable.ThenByDescending(propertyName);
}
}
}
}
}
if (orderedQueryable is null)
{
string defaultPropertyName = properties.Any(x => x.Name == EntityConnstants.DefaultKey) ? EntityConnstants.DefaultKey : properties.First().Name;
orderedQueryable = query.OrderByDescending(defaultPropertyName);
}
return orderedQueryable;
}
public static IQueryable<TEntity> PageBy<TEntity>(this IQueryable<TEntity> query, PagedRequest pagedRequest)
{
return query.Skip((pagedRequest.PageNumber - 1) * pagedRequest.PageSize).Take(pagedRequest.PageSize);
}
public static IQueryable<TEntity> SortAndPageBy<TEntity>(this IQueryable<TEntity> query, PagedAndSortedRequest? pagedAndSortedRequest = null)
{
pagedAndSortedRequest ??= new PagedAndSortedRequest { PageNumber = 1, PageSize = PagingConstants.DefaultPageSize };
return query.SortBy(pagedAndSortedRequest.OrderBy).PageBy(pagedAndSortedRequest);
}
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
{
return condition ? source.Where(predicate) : source;
}
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, int, bool>> predicate)
{
return condition ? source.Where(predicate) : source;
}
}

View File

@ -0,0 +1,44 @@
using System.Collections.Concurrent;
using System.Linq.Expressions;
namespace HelloShop.ServiceDefaults.Extensions;
public static class QueryableOrderByExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName) => OrderingHelper<T>.OrderBy(source, propertyName);
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName) => OrderingHelper<T>.OrderByDescending(source, propertyName);
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string propertyName) => OrderingHelper<T>.ThenBy(source, propertyName);
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string propertyName) => OrderingHelper<T>.ThenByDescending(source, propertyName);
private static class OrderingHelper<TSource>
{
private static readonly ConcurrentDictionary<string, LambdaExpression> cached = new();
public static IOrderedQueryable<TSource> OrderBy(IQueryable<TSource> source, string propertyName) => Queryable.OrderBy(source, (dynamic)CreateLambdaExpression(propertyName));
public static IOrderedQueryable<TSource> OrderByDescending(IQueryable<TSource> source, string propertyName) => Queryable.OrderByDescending(source, (dynamic)CreateLambdaExpression(propertyName));
public static IOrderedQueryable<TSource> ThenBy(IOrderedQueryable<TSource> source, string propertyName) => Queryable.ThenBy(source, (dynamic)CreateLambdaExpression(propertyName));
public static IOrderedQueryable<TSource> ThenByDescending(IOrderedQueryable<TSource> source, string propertyName) => Queryable.ThenByDescending(source, (dynamic)CreateLambdaExpression(propertyName));
private static LambdaExpression CreateLambdaExpression(string propertyName)
{
if (cached.TryGetValue(propertyName, out LambdaExpression? value))
{
return value;
}
var parameter = Expression.Parameter(typeof(TSource));
var body = Expression.Property(parameter, propertyName);
var keySelector = Expression.Lambda(body, parameter);
cached[propertyName] = keySelector;
return keySelector;
}
}
}

View File

@ -0,0 +1,10 @@
using System.Text.Json;
namespace HelloShop.ServiceDefaults;
public class PascalCaseNamingPolicy : JsonNamingPolicy
{
public static PascalCaseNamingPolicy PascalCase { get; } = new PascalCaseNamingPolicy();
public override string ConvertName(string name) => string.Concat(char.ToUpper(name[0]), name[1..]);
}

View File

@ -2,5 +2,5 @@
public class PagedAndSortedRequest : PagedRequest
{
public IEnumerable<SortingOrder>? Sorts { get; init; }
public string? OrderBy { get; init; }
}

View File

@ -1,8 +0,0 @@
namespace HelloShop.ServiceDefaults.Models.Paging;
public class SortingOrder
{
public required string PropertyName { get; init; }
public bool Ascending { get; init; }
}