使用ODataQueryOptions扩展导航属性

时间:2014-12-17 21:46:38

标签: asp.net-web-api odata

我正在构建一个必须公开从另一个第三方Web源检索的数据的OData v.4 Web服务,因此数据与LINQ世界中的任何内容都不相似,即:没有IQueryable,没有Context,没有任何东西。

要走的路似乎是从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()),但在返回之前

如何正确展开导航属性?

2 个答案:

答案 0 :(得分:3)

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

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

  • 您的控制器必须继承ODataController
  • IEdmModel必须与路线相关联。使用ODataModelBuilder(或ODataConventionModelBuilder)定义它。
  • 您的方法必须返回IQueryable<MyMasterEntity>
  • 仅声明[EnableQuery]。不要声明显式的ODataQueryOptions参数

答案 1 :(得分:2)

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

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

ODataProperties extesion方法在名称空间System.Web.OData.Extensions中定义。

这告诉OData格式化程序还要渲染selectexpand来输出。它无法触及扩展属性,否则会触发枚举。至少这是我理解的方式。

否则看起来你的方法是合理的。对我而言,可以这么说。我的情况是我有一个数据库第一个模型,我不能增加(例如视图之间的关系),所以我增加了OData模型,例如,expand手动增加&#34;外键链接& #34;和一对多关系,否则不在声明的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);