我刚刚在原始版本的基础上编写了 ODataQueryOption 的快速实现。不同之处在于 odata 的属性是从 HttpRequest 而不是原始版本中的 HttpRequestMessage 获取的。我仍然认为最好在 Web 服务器配置中增加最大请求 uri 长度并使用 GET 而不是 POST 和默认 ODataQueryOption ,我最终在我自己的项目中这样做了。
public class ODataQueryOptionsPost<T> : ODataQueryOptions<T>
{
private RawValues2 rawValues;
private IAssembliesResolver _assembliesResolver2;
public FilterQueryOption FilterQueryOption { get; set; }
public ODataQueryOptionsPost(ODataQueryContext context, HttpRequestMessage request, HttpRequest httpRequest) :
base(context, request)
{
if (context == null)
throw new Exception(nameof(context));
if (request == null)
throw new Exception(nameof(request));
if (request.GetConfiguration() != null)
_assembliesResolver2 = request.GetConfiguration().Services.GetAssembliesResolver();
_assembliesResolver2 =
this._assembliesResolver2 ?? (IAssembliesResolver) new DefaultAssembliesResolver();
this.rawValues = new RawValues2();
var filter = GetValue(httpRequest.Params, "$filter");
if (!string.IsNullOrWhiteSpace(filter))
{
rawValues.Filter = filter;
FilterQueryOption = new FilterQueryOption(filter, context);
}
var orderby = GetValue(httpRequest.Params, "$orderby");
if (!string.IsNullOrWhiteSpace(orderby))
{
rawValues.OrderBy = orderby;
OrderbyOption = new OrderByQueryOption(orderby, context);
}
var top = GetValue(httpRequest.Params, "$top");
if (!string.IsNullOrWhiteSpace(top))
{
rawValues.Top = top;
TopOption = new TopQueryOption(top, context);
}
var skip = GetValue(httpRequest.Params, "$skip");
if (!string.IsNullOrWhiteSpace(skip))
{
rawValues.Skip = skip;
SkipOption = new SkipQueryOption(skip, context);
}
var select = GetValue(httpRequest.Params, "$select");
if (!string.IsNullOrWhiteSpace(select))
{
rawValues.Select = select;
}
var inlinecount = GetValue(httpRequest.Params, "$inlinecount");
if (!string.IsNullOrWhiteSpace(inlinecount))
{
rawValues.InlineCount = inlinecount;
InlineCountOption = new InlineCountQueryOption(inlinecount, context);
}
var expand = GetValue(httpRequest.Params, "$expand");
if (!string.IsNullOrWhiteSpace(expand))
{
rawValues.Expand = expand;
}
var format = GetValue(httpRequest.Params, "$format");
if (!string.IsNullOrWhiteSpace(format))
{
rawValues.Format = format;
}
var skiptoken = GetValue(httpRequest.Params, "$skiptoken");
if (!string.IsNullOrWhiteSpace(skiptoken))
{
rawValues.SkipToken = skiptoken;
}
}
public InlineCountQueryOption InlineCountOption { get; set; }
public SkipQueryOption SkipOption { get; set; }
public TopQueryOption TopOption { get; set; }
public OrderByQueryOption OrderbyOption { get; set; }
private static string GetValue(NameValueCollection httpRequestParams, string key)
{
return httpRequestParams.GetValues(key)?.SingleOrDefault();
}
public override IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings)
{
if (query == null)
throw new Exception(nameof(query));
if (querySettings == null)
throw new Exception(nameof(querySettings));
IQueryable queryable = query;
if (this.FilterQueryOption != null)
queryable = this.FilterQueryOption.ApplyTo(queryable, querySettings, this._assembliesResolver2);
if (this.InlineCountOption != null && !this.Request.ODataProperties().TotalCount.HasValue)
{
long? entityCount = this.InlineCountOption.GetEntityCount(queryable);
if (entityCount.HasValue)
this.Request.ODataProperties().TotalCount = new long?(entityCount.Value);
}
OrderByQueryOption orderBy = this.OrderbyOption;
if (querySettings.EnsureStableOrdering &&
(this.Skip != null || this.Top != null || querySettings.PageSize.HasValue))
orderBy = orderBy == null
? GenerateDefaultOrderBy(this.Context)
: EnsureStableSortOrderBy(orderBy, this.Context);
if (orderBy != null)
queryable = (IQueryable) orderBy.ApplyTo(queryable, querySettings);
if (this.SkipOption != null)
queryable = this.SkipOption.ApplyTo(queryable, querySettings);
if (this.TopOption != null)
queryable = this.TopOption.ApplyTo(queryable, querySettings);
if (this.SelectExpand != null)
{
this.Request.ODataProperties().SelectExpandClause = this.SelectExpand.SelectExpandClause;
queryable = this.SelectExpand.ApplyTo(queryable, querySettings);
}
if (querySettings.PageSize.HasValue)
{
bool resultsLimited;
queryable = LimitResults(queryable as IQueryable<T>, querySettings.PageSize.Value, out resultsLimited);
if (resultsLimited && this.Request.RequestUri != (Uri) null &&
(this.Request.RequestUri.IsAbsoluteUri && this.Request.ODataProperties().NextLink == (Uri) null))
this.Request.ODataProperties().NextLink =
GetNextPageLink(this.Request, querySettings.PageSize.Value);
}
return queryable;
}
private static OrderByQueryOption GenerateDefaultOrderBy(ODataQueryContext context)
{
string rawValue = string.Join(",",
GetAvailableOrderByProperties(context)
.Select<IEdmStructuralProperty, string>(
(Func<IEdmStructuralProperty, string>) (property => property.Name)));
if (!string.IsNullOrEmpty(rawValue))
return new OrderByQueryOption(rawValue, context);
return (OrderByQueryOption) null;
}
private static OrderByQueryOption EnsureStableSortOrderBy(OrderByQueryOption orderBy, ODataQueryContext context)
{
HashSet<string> usedPropertyNames = new HashSet<string>(orderBy.OrderByNodes.OfType<OrderByPropertyNode>()
.Select<OrderByPropertyNode, string>((Func<OrderByPropertyNode, string>) (node => node.Property.Name)));
IEnumerable<IEdmStructuralProperty> source = GetAvailableOrderByProperties(context)
.Where<IEdmStructuralProperty>(
(Func<IEdmStructuralProperty, bool>) (prop => !usedPropertyNames.Contains(prop.Name)));
if (source.Any<IEdmStructuralProperty>())
{
orderBy = new OrderByQueryOption(orderBy.RawValue, context);
foreach (IEdmStructuralProperty structuralProperty in source)
orderBy.OrderByNodes.Add((OrderByNode) new OrderByPropertyNode((IEdmProperty) structuralProperty,
OrderByDirection.Ascending));
}
return orderBy;
}
private static IEnumerable<IEdmStructuralProperty> GetAvailableOrderByProperties(ODataQueryContext context)
{
IEdmEntityType elementType = context.ElementType as IEdmEntityType;
if (elementType != null)
return (IEnumerable<IEdmStructuralProperty>) (elementType.Key().Any<IEdmStructuralProperty>()
? elementType.Key()
: elementType.StructuralProperties()
.Where<IEdmStructuralProperty>(
(Func<IEdmStructuralProperty, bool>) (property => property.Type.IsPrimitive())))
.OrderBy<IEdmStructuralProperty, string>(
(Func<IEdmStructuralProperty, string>) (property => property.Name));
return Enumerable.Empty<IEdmStructuralProperty>();
}
internal static Uri GetNextPageLink(HttpRequestMessage request, int pageSize)
{
return GetNextPageLink(request.RequestUri, request.GetQueryNameValuePairs(), pageSize);
}
internal static Uri GetNextPageLink(Uri requestUri, IEnumerable<KeyValuePair<string, string>> queryParameters,
int pageSize)
{
StringBuilder stringBuilder = new StringBuilder();
int num = pageSize;
foreach (KeyValuePair<string, string> queryParameter in queryParameters)
{
string key = queryParameter.Key;
string str1 = queryParameter.Value;
switch (key)
{
case "$top":
int result1;
if (int.TryParse(str1, out result1))
{
str1 = (result1 - pageSize).ToString((IFormatProvider) CultureInfo.InvariantCulture);
break;
}
break;
case "$skip":
int result2;
if (int.TryParse(str1, out result2))
{
num += result2;
continue;
}
continue;
}
string str2 = key.Length <= 0 || key[0] != '$'
? Uri.EscapeDataString(key)
: 36.ToString() + Uri.EscapeDataString(key.Substring(1));
string str3 = Uri.EscapeDataString(str1);
stringBuilder.Append(str2);
stringBuilder.Append('=');
stringBuilder.Append(str3);
stringBuilder.Append('&');
}
stringBuilder.AppendFormat("$skip={0}", (object) num);
return new UriBuilder(requestUri)
{
Query = stringBuilder.ToString()
}.Uri;
}
}
public class RawValues2
{
public string Filter { get; set; }
public string OrderBy { get; set; }
public string Top { get; set; }
public string Skip { get; set; }
public string Select { get; set; }
public string InlineCount { get; set; }
public string Expand { get; set; }
public string Format { get; set; }
public string SkipToken { get; set; }
}
要使用它,我们需要当前的请求对象
[HttpPost]
public async Task<PageResult<TypeOfYourViewModel>> GetDataViaPost(ODataQueryOptions<TypeOfYourViewModel> options)
{
IQueryable<TypeOfYourViewModel> result = await GetSomeData();
var querySettings = new ODataQuerySettings
{
EnsureStableOrdering = false,
HandleNullPropagation = HandleNullPropagationOption.False
};
var optionsPost = new ODataQueryOptionsPost<TypeOfYourViewModel>(options.Context, Request, HttpContext.Current.Request);
var finalResult = optionsPost.ApplyTo(result, querySettings);
var uri = Request.ODataProperties().NextLink;
var inlineCount = Request.ODataProperties().TotalCount;
var returnedResult = (finalResult as IQueryable<T>).ToList();
return new PageResult<TypeOfYourViewModel>(
returnedResult,
uri,
inlineCount
);
}