在 MVC Core 中动态按查询字符串过滤?
Filter by query string dynamically in MVC Core?
我有一个这样的获取方法...
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers()
{
var queryString = HttpContext.Request.Query;
return await _context.Customers.Take(7).ToListAsync();
}
我想传递这样的查询字符串:
https://localhost:44315/api/customer?param1=1¶m2=String Value
我想执行此操作而不必在我的参数列表中声明每个参数。例如
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers(int param1, string param2)
{
var queryString = HttpContext.Request.Query;
return await _context.Customers.Take(7).ToListAsync();
}
我想避免这样做,因为我的 class 有几十个参数。我知道您可以使用 [FromQuery] Customer customer
进行绑定,但我认为这不是我想要的。
有没有办法动态地做到这一点?
如果您只想通过 HttpContext 访问查询参数而忘记操作参数,则可以完全省略参数。 ASP.NET Core 不会将查询字符串评估为操作路由的一部分(这也意味着您不能基于查询参数创建重载,除非您通过属性路由指定不同的路由)。
我最终做了这样的事情
//GET: api/customer
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers()
{
var queryParams = HttpContext.Request.Query;
var collection = _context.Customers.FilterByQueryParams(queryParams);
return await Task.FromResult(collection.ToList());
}
然后在单独的 class
public static class DynamicFilterExtensionMethods
{
public static IEnumerable<T> FilterByQueryParams<T>(this IEnumerable<T> collection, IQueryCollection queryParams) where T : new()
{
var classType = typeof(T);
var propList = classType.GetProperties();
//accountNumber ==> accountNumber, AccountNumber ==> accountNumber
var props = new Dictionary<string, PropertyInfo>(propList.Select(x => new KeyValuePair<string, PropertyInfo>(Char.ToLowerInvariant(x.Name[0]) + x.Name.Substring(1), x)));
foreach (var param in queryParams) {
if (props.ContainsKey(param.Key)) {
var prop = props[param.Key];
if (prop.PropertyType.IsPrimitive) {
if (param.Value.Count == 1)
{
collection = collection.Where(x => prop.GetValue(x, null).ToString() == param.Value.First());
}
else {
var aggregate = new List<T>();
foreach (var value in param.Value) {
aggregate = aggregate.Union<T>(collection.Where(x => prop.GetValue(x, null).ToString() == value)).ToList();
}
collection = aggregate.AsEnumerable();
}
}
}
}
return collection;
}
}
这里还有很多事情要做,但我认为这是一个好的开始。
我有一个这样的获取方法...
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers()
{
var queryString = HttpContext.Request.Query;
return await _context.Customers.Take(7).ToListAsync();
}
我想传递这样的查询字符串:
https://localhost:44315/api/customer?param1=1¶m2=String Value
我想执行此操作而不必在我的参数列表中声明每个参数。例如
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers(int param1, string param2)
{
var queryString = HttpContext.Request.Query;
return await _context.Customers.Take(7).ToListAsync();
}
我想避免这样做,因为我的 class 有几十个参数。我知道您可以使用 [FromQuery] Customer customer
进行绑定,但我认为这不是我想要的。
有没有办法动态地做到这一点?
如果您只想通过 HttpContext 访问查询参数而忘记操作参数,则可以完全省略参数。 ASP.NET Core 不会将查询字符串评估为操作路由的一部分(这也意味着您不能基于查询参数创建重载,除非您通过属性路由指定不同的路由)。
我最终做了这样的事情
//GET: api/customer
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers()
{
var queryParams = HttpContext.Request.Query;
var collection = _context.Customers.FilterByQueryParams(queryParams);
return await Task.FromResult(collection.ToList());
}
然后在单独的 class
public static class DynamicFilterExtensionMethods
{
public static IEnumerable<T> FilterByQueryParams<T>(this IEnumerable<T> collection, IQueryCollection queryParams) where T : new()
{
var classType = typeof(T);
var propList = classType.GetProperties();
//accountNumber ==> accountNumber, AccountNumber ==> accountNumber
var props = new Dictionary<string, PropertyInfo>(propList.Select(x => new KeyValuePair<string, PropertyInfo>(Char.ToLowerInvariant(x.Name[0]) + x.Name.Substring(1), x)));
foreach (var param in queryParams) {
if (props.ContainsKey(param.Key)) {
var prop = props[param.Key];
if (prop.PropertyType.IsPrimitive) {
if (param.Value.Count == 1)
{
collection = collection.Where(x => prop.GetValue(x, null).ToString() == param.Value.First());
}
else {
var aggregate = new List<T>();
foreach (var value in param.Value) {
aggregate = aggregate.Union<T>(collection.Where(x => prop.GetValue(x, null).ToString() == value)).ToList();
}
collection = aggregate.AsEnumerable();
}
}
}
}
return collection;
}
}
这里还有很多事情要做,但我认为这是一个好的开始。