【发布时间】:2021-01-11 05:47:41
【问题描述】:
我正在开发一个 .Net 核心 API,并且有一个可以采用可选查询参数的分页端点。例如,端点列出项目,您还可以指定过滤包含或等于某个值的字段。
https://endpoint/items?pageparams=pageData&field1Contains=value&field1Equals=value&field2Contains=value&field2Equals=value
[HttpGet]
[Authorize(AuthenticationSchemes = AuthSchemes)]
[Route("items")]
public async Task<IActionResult> GetPagedItems(
[FromQuery] PageParameters pageParameters,
[FromQuery] OptionalSearchParams searchParameters)
{
// logic
}
目前我将 contains 视为比 equals 高阶,因此如果您为同一字段同时传递 'equals' 和 'contains',则将应用 'contains' 而忽略 'equals'。
API 还在动态构建查询和 where 子句,出于明显的安全原因,我真的不想这样做。
var searchClauses = new List<string>();
if (!string.IsNullOrEmpty(searchParameters.Field1Contains))
{
searchClauses.Add($" ([Field1] LIKE '%{searchParameters.Field1Contains}%')");
}
else if (!string.IsNullOrEmpty(searchParameters.Field1Equals))
{
searchClauses.Add($" ([Field1] = '{searchParameters.Field1Equals}') ");
}
// logic to read other optional params and add where clauses to list
var query = @$"SELECT TOP {pageParameters.PageSize}
-- statement removed for brevity --
FROM [SomeTable]
ORDER BY [Field]
OFFSET {((pageParameters.PageNumber - 1) * pageParameters.PageSize)} ROW)
BASE
WHERE {string.Join(" AND ", searchClauses)}
我的问题是我不知道如何将这些值作为参数/存储过程传递给数据库,因为它们并不总是被使用,并且有基于优先级应用的逻辑。不完全正确...我确实设法构建了一个 SQL 查询,但它体积庞大,并且 where 子句中有太多嵌套的 case/select 语句,这将是维护/性能影响的噩梦。
是否有更好的方法来处理可选字段过滤器,同时以参数化方式进行分页?我真的不想从数据库中获取所有项目然后进行过滤,因为可能会返回数千个项目。
【问题讨论】:
-
你的 c# 代码应该有一个所有参数和类型(int、varchar、...)的列表。然后根据用户选择的参数,您可以从列表中选择项目。为了更通用,您可以使用 GetSchema 方法,该方法将从 SQL 数据库中获取表、列、类型。
-
不是我想要的,是的,所有字段和类型都知道,但我无法选择传递存储过程的参数,它们是具体的。我还需要根据 contains/equals 要求进行更改。
-
sql server 中的输入参数可以为空。因此,在存储过程中,您可以测试输入参数是否为 null,并设置为默认值或忽略。
-
@MattSykes,必读:Dynamic Search Conditions in T‑SQL,作者 Erland Sommarskog。我个人在我的系统中经常使用带有 OPTION (RECOMPILE) 的静态 SQL。
标签: c# sql asp.net-core