我正在通过Microsoft.AspNetCore.OData
包开始使用OData。成功的案例是可行的,但是我注意到当成功返回IQueryable<T>
但却无法执行时,我没有收到正确的错误消息。相反,我得到了一个被截断的结果:
{"@odata.context":"https://localhost:44300/odata/$metadata#Documents","value":[
我的控制器没有什么特别之处:
public class DocumentsController : ODataController
{
Db.Context Context { get; }
public DocumentsController(Db.Context context)
{
Context = context;
}
[EnableQuery]
public ActionResult<IQueryable<Document>> Get()
{
return Context.Documents;
}
}
问题不是特定于OData的:返回ControllerBase
的普通IQueryable<Document>
派生的控制器的行为方式相同(除了截断结果仅为[
以外)。但是,OData的上下文可能会排除一些可能的解决方案。
我知道为什么会这样:发送序列化结果已经开始;没有办法退回到原先的状态,而是发送错误响应。
我还理解,在通常情况下,这不是一个真正的选择:发送许多项目时,除了最后一个项目之外的所有项目都可以毫无问题地发送,并且在最后一个项目上可能会抛出一些异常,因此通常情况下,要防止出现这种情况,就需要缓冲整个结果。
但是,应该至少可以处理甚至在IQueryable<T>
返回其第一个结果之前引发异常的情况。这将涵盖最严重的错误,在这些错误中,良好的异常消息对于调试非常有帮助:数据库脱机,数据库架构不符合预期等。
我该怎么做?
答案 0 :(得分:0)
您可以直接在控制器中调用查询选项,而不必使用[Queryable]属性。它将允许您执行所需的任何异常处理,并在需要时构建自定义响应。
为此,请将ODataQueryOptions参数添加到控制器方法。在这种情况下,您不需要[Queryable]属性。有关更多详细信息,请访问https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options#invoking-query-options-directly
答案 1 :(得分:0)
我可以创建一个自定义的IEnumerable<T>
实现,该实现包装另一个IEnumerable<T>
,并在构造后立即调用.GetEnumerator().MoveNext()
,注意保存枚举数和MoveNext()
的响应。然后,当第一次调用 my GetEnumerator()
时,我返回保存的枚举数周围的包装器。首次调用其MoveNext()
时,我返回保存的布尔值。其他用途仅用于包装枚举和包装枚举器。我将保留代码,因为实现几乎是微不足道的。
棘手的部分是找到包装可查询对象的正确点。必须在应用URL中的任何查询修饰符之后。这里有两个主要选项:
EnableQueryAttribute
。相反,正如Ihar Yakimush回答的那样,我可以采用ODataQueryOptions<Document>
参数并直接应用查询修饰符。由于修改查询后仍处于控制状态,因此可以将其包装在自定义枚举中。子类EnableQueryAttribute
并覆盖其OnResultExecuting(ResultExecutingContext context)
方法。然后,我可以检查是否context.Result is ObjectResult
,如果可以,则检查objectResult.Value
是否为IEnumerable<T>
。如果两者都正确,那么我可以包装结果。
此方法的一种变体是将OnResultExecuting
放在单独的ActionFilterAttribute
派生词中,但对我来说将它们放在同一类中更有意义。
在两种情况下,我都必须照顾没有得到IQuerable<Document>
的情况:我可能得到IQueryable<SomeWrapperAround<Document>>
。处理起来很容易。 dynamic
似乎异常简单:只需创建object Wrap(object o) => o
和object Wrap<T>(IQueryable<T> queryable) => new EnumerableWrapper<T>(queryable)
重载,然后让运行时找出要调用的方法,但不幸的是SomeWrapperAround
可以OData内部类,dynamic
不喜欢。手动反射仍然不太困难。