OData Client中DataServiceQuery中的Expand错误

Mat*_*hew 5 expand client json odata

我正在测试OData .Net客户端,如下所示:

static void Main(string[] args)
    {
        var dataServiceContext = new Container(new Uri("http://localhost.fiddler:6851/"));

        var selectedOrders = from order in dataServiceContext.Orders.Expand("OrderDetails")
                                where order.OrderId==1
                                select order;

        DataServiceCollection<Order> orders = new DataServiceCollection<Order>(selectedOrders);        

        // Update Navigation
        orders[0].OrderDetails[0].Quantity=9999;

        dataServiceContext.SaveChanges();
    }
Run Code Online (Sandbox Code Playgroud)

而且,我得到了一个例外

"When writing a JSON response, a user model must be specified and the entity set and entity type must be passed to the ODataMessageWriter.CreateODataEntryWriter method or the ODataFeedAndEntrySerializationInfo must be set on the ODataEntry or ODataFeed that is being written."

我还通过浏览器测试了相同的服务

http://localhost:6851/Orders(1)?$expand=OrderDetails
Run Code Online (Sandbox Code Playgroud)

然后,服务返回

{ "@odata.context":"http://localhost:6851/$metadata#Orders/$entity","OrderId":1,"CustomerId":"KKKK9","OrderDetails":[
   {
      "OrderDetailId":1,"OrderId":1,"ProductId":1,"UnitPrice":10.00,"Quantity":1
   },
   {
      "OrderDetailId":2,"OrderId":1,"ProductId":2,"UnitPrice":20.00,"Quantity":2
   },
   {
      "OrderDetailId":6,"OrderId":1,"ProductId":3,"UnitPrice":30.00,"Quantity":33
   }
]}
Run Code Online (Sandbox Code Playgroud)

Fiddler还将相同的json数据捕获到客户端程序,但是客户端模块不读取它,并引发异常。

如果我删除了“扩展”,它就可以正常工作。

我究竟做错了什么 ?

oft*_*lit 0

我可以使用 TripPin OData V4 示例服务在 .NET Core 3.1 中重现该问题,如下所示:

using System;
using System.Net.Http;
using Simple.OData.Client;

namespace odata_simpleclient
{
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            HttpClientHandler clientHandler = new HttpClientHandler();
            clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
            HttpClient httpClient = new HttpClient(clientHandler);
            var settings = new ODataClientSettings(httpClient);
            settings.BaseUri = new Uri("https://services.odata.org/TripPinRESTierService/(S(juqvmnem2qpijynyewk4pld5))/");

            var client = new ODataClient(settings);
            var result = await client
                .For("People")
                .Key("russellwhyte")
                .NavigateTo("Trips")
                .FindEntriesAsync();

            Console.WriteLine(result);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

发生异常:CLR/Microsoft.OData.ODataException 写入 JSON 响应时,必须指定用户模型,并且必须将实体集和实体类型传递给 ODataMessageWriter.CreateODataResourceWriter 方法,或者必须在 ODataResource 或 ODataResourceSet 上设置 ODataResourceSerializationInfo正在写。

原因:

此问题会影响实体集中没有 NavigationPropertyBinding 的情况

当尝试另一个服务端点时它确实有效。

settings.BaseUri = new Uri("https://services.odata.org/v4/TripPinServiceRW/");
Run Code Online (Sandbox Code Playgroud)

与后者的区别在于ContainsTarget="true"NavigationProperty。

<NavigationProperty Name="Trips" Type="Collection(Microsoft.OData.Service.Sample.TrippinInMemory.Models.Trip)" />
<NavigationProperty Name="Trips" Type="Collection(Microsoft.OData.SampleService.Models.TripPin.Trip)" ContainsTarget="true" />
Run Code Online (Sandbox Code Playgroud)

这可以通过使用属性注释数据模型来添加Contained

[Contained]
public List<Trip> Trips { get; set; }
Run Code Online (Sandbox Code Playgroud)

作为替代方案,也可以使用 ODataConventionModelBuilder 添加包含关系。

var modelBuilder = new ODataConventionModelBuilder();
modelBuilder
    .EntityType<Person>()
    .ContainsMany(p => p.Trips);
Run Code Online (Sandbox Code Playgroud)