好的,答案不是序列化程序。
当您添加EnableQuery时,默认情况下允许OData使用Select、Count、Skip、Top、Expand等不同的方法。但更重要的是,EnableQuery是一个ActionFilterAttribute,这意味着:
ASP.NET Core中的过滤器允许在请求处理管道的特定阶段之前或之后运行代码。
请查看此处以获取有关
ActionFilters的更多信息。
也就是说,EnableQuery覆盖了两种方法(Before/After)。
public override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
并且
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
第一个函数是创建和验证查询选项。这需要进行大量的反射工作。
第二个函数检查结果是否已设置并成功,例如如果您返回IQueryable,则在此处执行。查询在此级别上被实体化。
它首先尝试从返回响应消息中检索IQueryable。然后根据“EnableQueryAttribute”上的验证设置验证uri中的查询。最后适当地应用查询,并将其重置回响应消息。
正如您所看到的,所有这些额外的逻辑比仅将结果转换为JSON输出更加复杂。
我采用了您的示例:
[ApiController]
[Route("api/test")]
public class WeatherForecastController : ControllerBase
{
[Route("/get1")]
[HttpGet]
[EnableQuery(EnsureStableOrdering = false)]
public ActionResult<IEnumerable<Person>> Get1()
{
var list = new List<Person>();
for (var i = 0; i < 2500; i++)
{
list.Add(new Person());
}
return list;
}
[Route("/get2")]
[HttpGet]
public IActionResult Get2()
{
var list = new List<Person>();
for (var i = 0; i < 2500; i++)
{
list.Add(new Person());
}
var json = JsonConvert.SerializeObject(list);
return Ok(json);
}
[Route("/get3")]
[HttpGet]
public IActionResult Get3()
{
var list = new List<Person>();
for (var i = 0; i < 2500; i++)
{
list.Add(new Person());
}
return Ok(list);
}
我使用20个不同的线程对这些端点中的每一个进行了相同的请求性能测试:get1,get2,get3,结果如下:
每个端点的平均毫秒数为:433、355、337。在我看来,这并不差,第一个是Odata,与最后一个相比,仅有96毫秒的差异,针对此负载测试。
我不确定为什么您的示例在此处需要30-40秒,因为我使用了您相同的代码和Jmeter进行负载测试,我得到的最长时间为900毫秒,仅限于第一个请求,并且这是有道理的,因为apppool是从第一个请求开始启动的,如果它处于休眠状态。
在我看来,如果你想实现odata可以进行的所有操作(包括塑形、排序、分页和过滤),你需要大量使用反射,至少对于塑形和过滤是这样,更不用说所有可用的二进制运算符了。
对我来说,创建自己的语法不是一个选项,你需要创建一个词法分析器和解析器来适应你自己的语法。因此,我认为你从中获得的好处是巨大的,除非你的API不需要这些复杂的运算符。还有一件事要考虑,就是如何扩展你的API,以避免影响性能,但这取决于你用来托管它的基础架构。
希望这能帮到你。