从NetCoreApp2.1调用OData服务时出现System.NotSupportedException

时间:2018-10-29 10:55:41

标签: .net-core odata

我已经建立了一个多目标(net4.5.2 / netstandard2)类库,可以使用我们的企业OData服务之一。 要访问此OData服务,我们使用OData v4 Client Code Generator (v7.5.0)

生成的代理类

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

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

  

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

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

看看Microsoft.OData.Client v7.5.0源代码,此行为似乎是设计使然并带有对.Net Core案例的特定处理。

我错过了什么吗?

以下代码可防止此问题,但几乎无法使用:

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而不添加特定代码?

2 个答案:

答案 0 :(得分:2)

如错误消息中所述,平台仅支持异步提取。即使使用了该功能,您也可能需要多次枚举结果-每次执行ToList()FirstOrDefault()或其他类似的System.Generics.Collections操作时,您实际上都会得到{ {1}}并对其进行枚举。

我采用了这种解决方案:从OData库中获取可枚举的结果后,立即对其进行枚举并将其放入由我实例化的另一个可枚举的容器(在这种情况下为Enumerator)。

Dictionary<string, MyAwesomeResult>

然后,我使用实例化的容器-我不再需要通过枚举返回的可枚举再次枚举 var resultsQuery = this.oDataClient.MyAwesomeResults .AddQueryOption("$filter", "Name eq 'MyAwesomeName'") .AddQueryOption("$top", "5") .AddQueryOption("$skip", "2"); IEnumerable<MyAwesomeResult> resultsRaw = await resultsQuery.ExecuteAsync(); var results = new Dictionary<string, MyAwesomeResult>();` foreach (var resultRaw in resultsRaw) { results.Add(resultRaw.Key, resultRaw); }

答案 1 :(得分:0)

如@PanagiotisKanavos DataServiceQuery.ToString()所述,将返回OData查询的uri。 基于此,我编写了自己的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,处理身份验证令牌并执行结果反序列化。 当我在接口上处理IQueryables时,FinalType会跟踪我要获取和反序列化的类型。 然后我写了自己的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的{​​{1}}方法,请等待并反序列化结果:

HttpClient

最后,我将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

然后,我可以像使用标准var myQueryable = new ODataLinqQuery<MyData>(this.Container.MyDataSet, myclient, typeof(MyData)); 一样应用过滤器,orderby,top和skip并获取结果。我知道此实现尚不完整,对OData的IQueryable不如对SQL的大多数IQueryable完整,但它达到了我所需的最低要求。