Как сказал @PanagiotisKanavos DataServiceQuery.ToString()
вернет URI запроса OData.
Исходя из этого, я написал свой 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
"просто" вызывает метод GetAsync
его базового 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 и получить результаты как со стандартным IQueryable
. Я знаю, что эта реализация не завершена, и IQueryable
для OData не так полна, как большинство IQueryable
для SQL, но достигает минимума, который мне нужен.