【问题标题】:Expanding navigation properties with ODataQueryOptions使用 ODataQueryOptions 扩展导航属性
【发布时间】:2015-02-16 13:56:34
【问题描述】:

我正在构建一个 OData v.4 Web 服务,该服务必须公开从另一个 3rd 方 Web 源检索到的数据,因此该数据与 LINQ 世界中的任何内容都不同,即:没有 IQueryable、没有上下文、没有任何东西..

要走的路似乎是手动处理来自 ODataQueryOptions 的参数并返回简单的项目序列。所以,控制器方法应该是这样的:

class MyMasterEntity {
    [Contained]
    public IEnumerable<MyDetailEntity> Details { get; set; }
}

// [EnableQuery]
public IEnumerable<MyMasterEntity> Get(ODataQueryOptions<MyMasterEntity> options)
{
   // process .FilterQueryOption
   // process .SelectExpandQueryOption
   // process .SkipQueryOption
   // process .TopQueryOption
   return myMasterEntityList;
}

这很好用,除了$expand=Details,在这种情况下属性不会在结果响应中扩展,尽管我的逻辑添加它们就好了。

如果添加[EnableQuery] 属性(一开始是没有意义的,因为它与ODataQueryOptions 的整个想法是互斥的),然后扩展开始工作。或者它假装正在工作,因为真正发生的是查询被处理了两次:第一次由我的代码处理,然后在我返回数据后由 OData 机器处理。这可能是可以容忍的(无论如何,我手动进行了昂贵的调用,所以没有什么大的 OData 重试已经准备好的数据),如果不是因为第二次通过诸如 $skip 之类的非确定性操作。 (即:可以根据需要多次应用 $top 以获得相同的结果,但不能使用 $skip 执行此操作)。

正如我从逆向工程相关程序集中了解到的那样,标准扩展代码将实体包装成某种东西,这告诉 JSON 格式化程序发出相应的属性,而不管它们是否实际上在实体内部展开。

也试过了:

  • 更改返回类型(IQueryable、IHttpActionResult)
  • 在手动展开后但返回之前强制调用SelectExpandQueryOption.ApplyTo(myMasterEntityList, new ODataQuerySettings())

如何正确展开导航属性?

【问题讨论】:

    标签: asp.net-web-api odata


    【解决方案1】:

    为什么要手动处理查询选项?

    使用AsQueryable LINQ 扩展方法将您的数据转换为可查询 集合(实际上您正在使用LINQ to Object)。

    • 你的控制器必须继承自ODataController
    • IEdmModel 必须与路由相关联。使用 ODataModelBuilder(或 ODataConventionModelBuilder)定义它。
    • 你的方法必须返回IQueryable&lt;MyMasterEntity&gt;
    • 只声明[EnableQuery]。不要显式声明 ODataQueryOptions 参数

    【讨论】:

      【解决方案2】:

      相信你需要引入如下一段代码

      if(options.SelectExpand != null)
      {
          Request.ODataProperties().SelectExpandClause = options.SelectExpand.SelectExpandClause;
      }
      

      ODataProperties 扩展方法在命名空间System.Web.OData.Extensions中定义。

      这告诉 OData 格式化程序也将 selectexpand 渲染到输出。否则它不能触及扩展的属性,因为它可能会触发枚举。至少我是这么理解的。

      否则,您的方法看起来是一种合理的方法。可以这么说,对我有用。我的情况是我有一个无法扩充的数据库优先模型(例如视图之间的关系),所以我扩充了 OData 模型,例如,expand 手动添加“外键链接”和一对多否则不在声明的 edmx 模型中的关系。然后在可能的情况下,我希望 OData 查询达到 DB 级别(例如 filter),所以我所做的大致如下

      public async Task<IHttpActionResult> Get(ODataQueryOptions<T> options)
      {
          IQueryable<T> tempQuery = initialQuery; //E.g. EfContext.T
          IQueryable<T result = tempQuery;
      
          if(options.SelectExpand != null)
          {
              if(options.Filter != null)
              {
                  tempQuery = options.Filter.ApplyTo(tempQuery, new ODataQuerySettings()) as IQueryable<T>;   
              }
              /* Other options that should go to the DB level... */
      
              //The results need to be materialized, or otherwise EF throws a
              //NotSupportedException due to columns and properties that aren't in the DB...
              result = (await tempQuery.ToListAsync()).AsQueryable();
      
              //Do here the queries that can't be hit straight by the queries. E.g.
              //navigation properties not defined in EF views (that can't be augmented)...
      
              //This is needed to that OData formatter knows to render navigation links too.
              Request.ODataProperties().SelectExpandClause = options.SelectExpand.SelectExpandClause;
      }
      
      return Ok(result);
      

      【讨论】:

      • 宾果游戏!分配 Request.ODataProperties().SelectExpandClause 为我做了。目前这是一个令人惊讶的模糊领域,可能是因为 OData v4 相对新颖(?)。我只设法将问题追溯到 JsonConverterAttribute,但后来卡住了,尤其是因为 ILSpy 在尝试“分析...”相关类时开始惨遭失败。
      • 同上!我喜欢使用查询“命中数据源”而无需为所有选项定义大量各种“资源”或端点的能力。尤其是现在我在关系数据库中有视图,我将查询作为“命名”查询并且可以轻松地跟踪它们。我希望这些示例将更多地集中在从各个地方加载数据的方面,但仍然对源应用可查询过滤。
      猜你喜欢
      • 1970-01-01
      • 2012-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-13
      • 2021-03-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多