从 NetCoreApp2.1 调用 OData 服务时出现 System.NotSupportedException [英] System.NotSupportedException when calling OData service from NetCoreApp2.1

查看:38
本文介绍了从 NetCoreApp2.1 调用 OData 服务时出现 System.NotSupportedException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我设置了一个多目标 (net4.5.2/netstandard2) 类库,允许使用我们的企业 OData 服务之一.要访问此 OData 服务,我们使用由 OData v4 客户端代码生成器 (v7.5.0)

I have set up a multi targetting (net4.5.2/netstandard2) class library allowing to consume one of our enterprise OData services. To access this OData service we use a proxy class generated with the OData v4 Client Code Generator (v7.5.0)

不幸的是,当我尝试在 Netcoreapp2.1 应用程序中使用我的库时,我会在尝试枚举集合时遇到问题.

Unfortunately, when trying to use my library in a Netcoreapp2.1 application I encounter an issue as soon as I try to enumerate a collection.

Container.MyDataSet.ToList(); 产生以下异常:

"System.NotSupportedException : 此目标框架未启用您可以直接枚举数据服务查询.这是因为枚举自动向数据发送同步请求服务.因为这个框架只支持异步操作,您必须改为调用 BeginExecute 和 EndExecute 方法来获取支持枚举的查询结果."

"System.NotSupportedException : This target framework does not enable you to directly enumerate over a data service query. This is because enumeration automatically sends a synchronous request to the data service. Because this framework only supports asynchronous operations, you must instead call the BeginExecute and EndExecute methods to obtain a query result that supports enumeration."

在 .Net 4.5.2 应用程序中使用相同的多目标库时,我没有遇到此问题.

I do not encounter this issue when using this same multitarget library in a .Net 4.5.2 application.

查看 Microsoft.OData.Client v7.5.0 源代码,此行为似乎是设计使然,具体处理 .Net Core 案例.

Having a look at the Microsoft.OData.Client v7.5.0 source code, this behaviour seems to be by design with specific handling of the .Net Core case.

我错过了什么吗?

下面的代码防止了这个问题,但几乎无法使用:

The following code prevents the issue, but it is barely usable :

var query = (DataServiceQuery<MyData>)Container.MyDataSet;
var taskFactory = new TaskFactory<IEnumerable<MyData>>();
var t = taskFactory.FromAsync(query.BeginExecute(null, null), data => query.EndExecute(data));
t.ConfigureAwait(false);
IEnumerable<MyData> result = t.Result;

如何在不添加特定代码的情况下在 .Net Core 应用程序中使用 OData IQueryable?

How can I use an OData IQueryable in .Net Core application without adding specific code ?

推荐答案

正如@PanagiotisKanavos 所说 DataServiceQuery.ToString() 将返回 OData 查询的 uri.基于此,我编写了自己的 IQueryable :

As said by @PanagiotisKanavos DataServiceQuery.ToString() will return the uri of the OData query. Based on this, I wrote my own IQueryable :

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

using Microsoft.OData.Client;

public class ODataLinqQuery<T> : IOrderedQueryable<T>
{
    public IQueryProvider Provider { get; }

    private DataServiceQuery<T> DataServiceQuery { get; }

    public ODataLinqQuery(DataServiceQuery<T> dataServiceQuery, MyClient client, Type finalType)
    {
        this.DataServiceQuery = dataServiceQuery;
        this.Provider = new ODataLinqQueryProvider<T>(dataServiceQuery, client, finalType);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return this.Provider.Execute<IEnumerable<T>>(this.Expression).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.Provider.Execute<System.Collections.IEnumerable>(this.Expression).GetEnumerator();
    }

    public Expression Expression => this.DataServiceQuery.Expression;

    public Type ElementType => typeof(T);
}

其中 MyClient 是一个实用程序类,用于包装 HttpClient、处理身份验证令牌并执行结果反序列化.FinalType 是跟踪我想要获取和反序列化的类型,因为我正在通过接口处理 IQueryables.然后我写了我自己的 IQueryProvider :

Where MyClient is an utility class which wraps an HttpClient, handles authentication token, and performs result deserialization. FinalType is to keep track on the type I want to obtain and deserialize, as I am handling IQueryables over interfaces. Then I wrote my own IQueryProvider :

using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;

using Microsoft.OData.Client;

public class ODataLinqQueryProvider<T> : IQueryProvider
{
    private MyClient Client { get; set; }

    private DataServiceQuery<T> DataServiceQuery { get; set; }

    private Type FinalType { get; }

    public ODataLinqQueryProvider(
        DataServiceQuery<T> dsq,
        MyClient client,
        Type finalType)
    {
        this.DataServiceQuery = dsq;
        this.Client = client;
        this.FinalType = finalType;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        return new ODataLinqQuery<T>(this.DataServiceQuery, this.Client, this.FinalType);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        var pro = new DataServiceQuery<TElement>(expression, this.DataServiceQuery.Provider as DataServiceQueryProvider);
        return new ODataLinqQuery<TElement>(pro, this.Client, this.FinalType);
    }

    public object Execute(Expression expression)
    {
        this.DataServiceQuery = new DataServiceQuery<T>(expression, this.DataServiceQuery.Provider as DataServiceQueryProvider);
        return this.Execute();
    }

    public TResult Execute<TResult>(Expression expression)
    {
        this.DataServiceQuery = new DataServiceQuery<T>(expression, this.DataServiceQuery.Provider as DataServiceQueryProvider);
        var res = this.Execute();
        if (typeof(IEnumerable).IsAssignableFrom(typeof(TResult)))
        {
            return (TResult)res;
        }
        else
        {
            return ((IEnumerable)res).Cast<TResult>().FirstOrDefault();
        }
    }

    private object Execute()
    {
        var result = Client.GetResult(typeof(OData<>).MakeGenericType(this.FinalType), HttpMethod.Get, new Uri(this.DataServiceQuery.ToString())) as OData;
        return result.Objects;
    }
}

其中 Odata<> 类仅用于 OData 结果的反序列化,GetResult只是"调用其底层的 GetAsync 方法HttpClient 使用正确的身份验证标头,等待并反序列化结果:

Where Odata<> class is just for deserialization of the OData result and GetResult "just" invokes the GetAsync method of its underlying HttpClient with the correct authentication headers, wait for and deserializes the result :

using System.Collections.Generic;

using Newtonsoft.Json;

public class OData<T> : OData where T : class
{
    public override IEnumerable<object> Objects => this.Value;

    public List<T> Value { get; set; }
}

public class OData
{
    [JsonProperty("@odata.context")]
    public string Metadata { get; set; }

    public virtual IEnumerable<object> Objects { get; set; }
}

最后我公开我的 IQueryable 如下:

Finally I expose my IQueryable as follows :

var myQueryable = new ODataLinqQuery<MyData>(this.Container.MyDataSet, myclient, typeof(MyData));

然后我可以像使用标准的 IQueryable 一样应用过滤器、orderby、top 和 skip 并获得结果.我知道这个实现并不完整,IQueryable 到 OData 不像大多数 IQueryable 到 SQL 那样完整,但它达到了我需要的最低限度.

I can then apply filters, orderby, top and skip and get the results as with a standard IQueryable. I know that this implementation is not complete, and IQueryable to OData is not as complete as most IQueryable to SQL, but it achieves the minimum I need.

这篇关于从 NetCoreApp2.1 调用 OData 服务时出现 System.NotSupportedException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆