分页排序和多条件查询
This commit is contained in:
parent
62a8b7f569
commit
6fef1a9ea0
@ -8,7 +8,7 @@ using HelloShop.ServiceDefaults.Models.Paging;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
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
|
// 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)]
|
[Authorize(IdentityPermissions.Users.Default)]
|
||||||
public async Task<ActionResult<PagedResponse<UserListItem>>> GetUsers([FromQuery] UserListRequest model)
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
users = users.WhereIf(model.PhoneNumber is not null, e => e.PhoneNumber == model.PhoneNumber);
|
||||||
|
|
||||||
var responseModel = new PagedResponse<UserListItem>(result, result.Count);
|
IQueryable<User> pagedUsers = users.SortAndPageBy(model);
|
||||||
|
|
||||||
return Ok(responseModel);
|
List<User> pagedUserList = await pagedUsers.ToListAsync();
|
||||||
|
int totalCount = await users.CountAsync();
|
||||||
|
|
||||||
|
return new PagedResponse<UserListItem>(mapper.Map<List<UserListItem>>(pagedUserList), totalCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
@ -120,7 +128,7 @@ namespace HelloShop.IdentityService.Controllers
|
|||||||
{
|
{
|
||||||
dbContext.Remove(user);
|
dbContext.Remove(user);
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,19 @@ namespace HelloShop.IdentityService.DataSeeding
|
|||||||
}
|
}
|
||||||
|
|
||||||
await userManager.AddToRoleAsync(guestUser, "GuestRole");
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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..]);
|
||||||
|
}
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
public class PagedAndSortedRequest : PagedRequest
|
public class PagedAndSortedRequest : PagedRequest
|
||||||
{
|
{
|
||||||
public IEnumerable<SortingOrder>? Sorts { get; init; }
|
public string? OrderBy { get; init; }
|
||||||
}
|
}
|
@ -1,8 +0,0 @@
|
|||||||
namespace HelloShop.ServiceDefaults.Models.Paging;
|
|
||||||
|
|
||||||
public class SortingOrder
|
|
||||||
{
|
|
||||||
public required string PropertyName { get; init; }
|
|
||||||
|
|
||||||
public bool Ascending { get; init; }
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user