我已经建立了一个多目标(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而不添加特定代码?
答案 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
完整,但它达到了我所需的最低要求。