我已经设置了一个多targetting (net4.5.2/netstandard2 2)类库,允许使用我们的企业OData服务之一。要访问这个OData服务,我们使用与OData v4客户端代码生成器(v7.5.0)一起生成的代理类
不幸的是,当我试图在Netcoreapp2.1应用程序中使用我的库时,当我试图枚举一个集合时,我就会遇到一个问题。
Container.MyDataSet.ToList();产生以下异常:
"System.NotSupportedException :此目标框架不能使您直接枚举数据服务查询。这是因为枚举会自动向数据服务发送同步请求。由于该框架只支持异步操作,因此必须调用BeginExecute和EndExecute方法来获得支持枚举的查询结果。“
在.Net 4.5.2应用程序中使用相同的多目标库时,我不会遇到这个问题。
查看一下Microsoft.OData.Client v7.5.0源代码,这种行为似乎是通过特定处理.Net核心案例来设计的。
我错过了什么吗?
下面的代码防止了这个问题,但它几乎无法使用:
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;如何在不添加特定代码的情况下在OData核心应用程序中使用IQueryable?
发布于 2018-11-16 14:07:06
正如@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,处理身份验证令牌,并执行结果反序列化。FinalType是跟踪我想要获取和反序列化的类型,因为我正在通过接口处理IQueryables。然后我写了我自己的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“只是”使用正确的身份验证头调用其基础HttpClient的GetAsync方法,则等待并反序列化结果:
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,并获得与标准IQueryable一样的结果。我知道这个实现是不完整的,IQueryable到OData并不像大多数IQueryable到SQL那样完整,但是它达到了我所需要的最小值。
发布于 2018-11-13 02:35:02
正如错误消息中提到的,该平台只支持异步获取。即使在使用它之后,您也可能需要多次枚举结果--每次执行ToList()、FirstOrDefault()或其他类似的System.Generics.Collections操作时,基本上都是获取集合的Enumerator并对其进行枚举。
我采用了这个解决方案:在从OData库中获取可枚举结果之后,我立即对它们进行枚举,并将它们放在由我实例化的另一个可枚举容器中(在本例中为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);
}然后,我使用实例化的容器--我不再需要对DataServiceQuery<MyAwesomeResult>.ExecuteAsync返回的枚举进行再次枚举。
https://stackoverflow.com/questions/53043994
复制相似问题