ASP.NET Core WebAPI在返回集合时是否不支持延迟执行/延迟评估?

问题描述 投票:3回答:1

在编写C#ASP.NET核心WebAPI控制器时,我刚刚遇到了我认为有点奇怪的行为。

考虑以下控制器方法,它接受JSON主体并生成XML输出(原因我不会进入):

[Produces("application/xml")]
[Route("api/route/to/data"]
[HttpPost]
public IActionResult GetData([FromBody]IEnumerable<InputData> inputs)
{
    return HandleResponse(() => _repository.GetData(inputs));
}

以下存储库方法:

public IEnumerable<OutputData> GetData(IEnumerable<InputData> inputs)
{
    foreach(var input in inputs)
    {
        string field1 = input.Field1;
        int field2 = input.Field2;

        const string sql = @"
            SELECT foo.bar, foo.baz
            FROM my_table foo
            WHERE foo.wubble = :field1
            AND foo.flob = :field2";

        yield return _repoBase.GetSingleValue<OutputData>(sql, new {field1, field2});
    }
}

(_repoBase.GetSingleValue方法只是Dapper的QueryFirstOrDefault方法的一个愚蠢的通用包装,并没有做任何花哨或复杂的事情。而且我知道以这种方式使用循环并不是最佳的,但它会为此目的而做这个例子。)

如果在控制器中放置断点,然后从Postman调用此端点,在主体中提供简单的JSON数据,例如:

[{"field1":"wibble", "field2":123456}]

然后断点被击中,并且repo按预期拉回数据,并且执行似乎失败 - 但Postman报告HTTP 406 Not Acceptable状态代码作为响应返回,并且实际上不包括数据。

但是,如果我强制IEnumerable在返回之前枚举到具体集合中:

[Produces("application/xml")]
[Route("api/route/to/data"]
[HttpPost]
public IActionResult GetData([FromBody]IEnumerable<InputData> inputs)
{
    return HandleResponse(() => _repository.GetData(inputs).ToList());
}

然后我得到200 OK以及我预期的数据。

我当然知道使用IEnumerables特别是yield return时延迟执行/延迟评估的概念,但是我希望框架足够智能,可以在将它们分散到世界之前迭代它提供的任何集合。这实际上是预期的行为,还是一个错误?

至少它在这种情况下返回的响应代码不是非常直观的IMO - 它让我检查我的中间件设置,认为我忘了添加输出格式化程序或其他东西。

编辑1:是的,我将输出包装到ObjectResult中。

编辑2:ObjectResult包装实际上是在这个方法中完成的 - 没什么特别的:

protected IActionResult HandleResponse<T>(Func<T> resultProvider)
{
    try
    {
        return new ObjectResult(resultProvider());
    }
    catch (OracleException oex)
    {
        // DB error handling - omitted for brevity.
        // Nothing is done to the collection here.
        int errorCode = 500 // This is actually mapped to whatever Oracle error code is returned, e.g. incorrect Oracle password = 403.
        return new StatusCodeResult(errorCode);
    }
    catch (Exception e)
    {
        _logger.LogError("Unhandled Exception", e);
        return new StatusCodeResult(500);
    }
}
c# asp.net-core dapper lazy-evaluation asp.net-core-webapi
1个回答
0
投票

由于上面的评论者,感谢姗姗来迟。

事实证明,此行为特定于XML输出格式化程序;他们似乎根本不支持产量支持的IEnumerable<T>对象的序列化,至少在.NET Core中是这样。

我尝试使用XmlSerializerOutputFormatterXmlDataContractSerializerOutputFormatter作为输出格式化程序,结果相同。

用XML输出格式化程序替换标准JSON格式化器可以解决问题。

在我的例子中,XML输出是必需的,所以作为一种解决方法,我根据我的原始帖子明确地将IEnumerable<T>转换为List<T>。然后,XML格式化程序可以正确地序列化集合。

© www.soinside.com 2019 - 2024. All rights reserved.