【问题标题】:Proper way to call nested Expand() from .NET OData 4 Client从 .NET OData 4 客户端调用嵌套 Expand() 的正确方法
【发布时间】:2021-05-12 23:04:30
【问题描述】:

问题

在 Web API 上的 OData 4 服务中,从 .NET 客户端调用嵌套 $expand 的正确方法是什么?我们正在使用OData Client Generator。回到之前的带有 OData 3 服务的 WCF 数据服务,我们可以调用 .Expand("Customers/Orders")。在带有 OData 4 的 Web API 中,如果您尝试 .Expand("Customers/Orders"),我们将无法再这样做并收到以下信息:

URI 中指定的查询无效。找到了一个遍历多个导航 > 属性的路径。请改写查询,使每个展开路径仅包含类型 >segments 和导航属性。

解决方法

我们可以通过调用 expand 来解决这个问题:.Expand("Customers($expand=Orders)")。在非嵌套的 $expand 场景中,我喜欢像 .Expand(d => d.Customers) 这样的 lambda 支持。 .NET OData 4 客户端中是否有适当的方法来调用嵌套扩展而没有不幸的.Expand("Customers($expand=Orders)") 魔术字符串?如果没有,是否有像“客户/订单”这样更简洁的字符串模式可以使用?谢谢。

【问题讨论】:

  • 在OData v3场景下可以看到这个MS recommendation
  • 感谢 .NET OData 4 客户端的解决方法: .Expand("Customers($expand=Orders)") 在 url 中,您可以像这样使用它: ?$expand=Customers($ expand=Orders) 参考:stackoverflow.com/a/28492946/2908623

标签: odata wcf-data-services asp.net-web-api astoria wcf-data-services-client


【解决方案1】:

您要发送的请求是:

GET http://host/service/Customers/Orders

对吗?

根据OData协议:

为了根据特定关系请求相关实体,客户端向源实体的请求 URL 发出 GET 请求,后跟正斜杠和表示关系的导航属性的名称.

因此,不支持此类请求,因为“/Orders”之前的“客户”是实体集的名称,而不是单个实体的名称。您只能编写嵌套扩展,如:

GET http://host/service/Customers(1)/Orders

使用OData V4代码生成器对应以下代码sn-ps:

var orders = context.Customers.ByKey(new Dictionary<string, object>() { { "ID", 1 } }).Orders.Execute();

您需要遍历所有客户才能获得他们的所有订单。

【讨论】:

  • @Yi_Ding,很高兴收到您的回复。但是,您可以重新阅读我的问题吗?看来你搞错话题了。当我说扩展时,我的意思是像 $expand 那样扩展。
【解决方案2】:

对此的一点扩展:

        public static DataServiceQuery<TSource> Expand<TSource,TNavigation,TExpand>(this DataServiceQuery<TSource> dataServiceQuery, Expression<Func<TSource, DataServiceCollection<TNavigation>>> expression,  Expression<Func<TNavigation,TExpand>> navigation)
    {
        var expressionName = (expression.Body as System.Linq.Expressions.MemberExpression).Member.Name;
        var navigationName = (navigation.Body as System.Linq.Expressions.MemberExpression).Member.Name;


        return dataServiceQuery.Expand($"{expressionName}($expand={navigationName})");
    }

现在你有了智能感知和类型检查

例子:

db.Container.Expand(c=> c.Customers, customer => customer.Orders)

【讨论】:

  • 这是一个很好的答案,但我对您实际调用此函数的方式感到头疼....
  • 想通了,这个特定的函数是用来处理一个实体的属性,它返回一个实体的+Collection+。所以要使用它,你可以这样做 r = container.MyProducts.Expand(p => p.Categories, category => category.CategoryType)
  • 是的,它用于集合属性中的嵌套对象(或集合)。您也可以像这样使用它: r = container.MyProducts.Expand(p => p.Customers, customer=> customer.Orders) (就像最初的问题一样)
【解决方案3】:

在 OData v4 中,扩展多个级别是无效的,例如您在问题中提到的:.Expand("Customers/Orders")。我认为客户端不会支持这样的 API。这是 ABNF http://docs.oasis-open.org/odata/odata/v4.0/os/abnf/odata-abnf-construction-rules.txt 中的内容:

expand            = '$expand' EQ expandItem *( COMMA expandItem )
expandItem        = STAR [ ref / OPEN levels CLOSE ]
                  / expandPath
                    [ ref   [ OPEN expandRefOption   *( SEMI expandRefOption   ) CLOSE ]
                    / count [ OPEN expandCountOption *( SEMI expandCountOption ) CLOSE ]
                    /         OPEN expandOption      *( SEMI expandOption      ) CLOSE 
                    ]
expandPath        = [ qualifiedEntityTypeName "/" ] 
                    *( ( complexProperty / complexColProperty ) "/" [ qualifiedComplexTypeName "/" ] )
                    navigationProperty 
                    [ "/" qualifiedEntityTypeName ]

【讨论】:

  • @Tan_Jinfu 谢谢,这是很好的信息。但它引出了一个问题,为什么 '$expand=Customers($expand=Orders)' 在 MS 实现中起作用?
  • 此外,System.Web.OData.EnableQueryAttribute 上有一个 MaxExpansionDepth property,它声明 获取或设置 $expand 查询选项的最大扩展深度。要禁用最大扩展深度检查,请将此属性设置为 0。
  • 因为$expand=Customers($expand=Orders)中的$expand=Orders被称为查询选项,而查询选项可以是$filter或者另一个$expand,而ABNF是:expandOption = expandRefOption / 选择 / 展开 / 级别
  • 这里清楚地记录了它为什么无效:docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/…
  • 在服务器端的 OData v4 的 MS 实现中支持嵌套 $expand 和 $select,但这里的问题是如何使用 OData 客户端生成器生成的代理构造这样的查询。 . 关键是使用Linq的select函数把我们要的具体字段选择成匿名类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-13
  • 2014-02-20
  • 2015-07-30
  • 2015-07-29
  • 1970-01-01
  • 2016-11-22
  • 2012-09-06
相关资源
最近更新 更多