I\'m implementing a Web API interface to support some fairly complex queries to run against it and have run up against an issue with the maximum request URI length.
Th
My two cents for dotnet core 2.2. Should also work on dotnet core 3.x but not guaranteed.
Handles all OData query parameters.
This passes the raw
argument from ODataActionParameters
to the HttpRequest
's Query
property (excluding the host), or if not present, we create one base of the ODataActionParameters
.
Extension for IQueryable{T}
which applies OData query options.
///
/// Extensions for interface.
///
public static class IQueryableExtensions
{
///
/// Apply the individual query to the given IQueryable in the right order, based on provided .
///
/// The instance.
/// The instance.
/// The instance.
/// The service provider.
/// The instance.
/// The entity type.
/// Returns instance.
public static IQueryable ApplyOData(this IQueryable self, HttpRequest request, ODataActionParameters actionParameters, IServiceProvider serviceProvider, ODataQuerySettings odataQuerySettings = default)
{
var queryOptionsType = typeof(ODataQueryOptions);
if (self is null)
{
throw new ArgumentNullException(nameof(self));
}
if (actionParameters is null)
{
throw new ArgumentNullException(nameof(actionParameters));
}
if (odataQuerySettings is null)
{
odataQuerySettings = new ODataQuerySettings();
}
var rawQuery = string.Empty;
if (actionParameters.ContainsKey("raw"))
{
rawQuery = HttpUtility.UrlDecode(actionParameters["raw"].ToString());
actionParameters.Remove("raw");
if (Uri.TryCreate(rawQuery, UriKind.Absolute, out Uri absRawQuery))
{
rawQuery = absRawQuery.Query;
}
request.Query = new QueryCollection(HttpUtility.ParseQueryString(rawQuery).ToDictionary());
}
else
{
request.Query = new QueryCollection(actionParameters.ToDictionary(k => $"${HttpUtility.UrlDecode(k.Key)}", v => new StringValues(HttpUtility.UrlDecode(v.Value.ToString()))));
}
//// request.QueryString = new QueryString("?" + string.Join("&", request.Query.Select(x => x.Key + "=" + x.Value)));
var edmModel = serviceProvider.GetRequiredService();
var odataQueryContext = new ODataQueryContext(edmModel, typeof(TEntity), null);
var odataQueryOptions = new ODataQueryOptions(odataQueryContext, request);
var queryOptionParser = new ODataQueryOptionParser(
edmModel,
edmModel.FindType(typeof(TEntity).FullName).AsElementType(),
edmModel.FindDeclaredNavigationSource(typeof(TEntity).FullName),
request.Query.ToDictionary(k => k.Key, v => v.Value.ToString()),
serviceProvider);
return odataQueryOptions.ApplyTo(self, odataQuerySettings);
}
}
In the example below you will need an extension for ActionConfiguration
like this:
//
/// Extensions for .
///
public static class ActionConfigurationExtensions
{
///
/// Adds OData parameters to the .
///
/// The instance.
/// Returns current instance.
public static ActionConfiguration AddODataParameters(this ActionConfiguration actionConfiguration)
{
foreach (var name in typeof(ODataRawQueryOptions).GetProperties().Select(p => p.Name.ToLower()))
{
actionConfiguration
.Parameter(name)
.Optional();
}
actionConfiguration
.Parameter("raw")
.Optional();
return actionConfiguration;
}
}
Example how to use it:
builder.EntityType()
.Collection
.Action(nameof(ExampleController.GetExamples))
.ReturnsCollectionFromEntitySet("Examples")
.AddODataParameters();
[HttpPost]
public ActionResult> GetExamples(ODataActionParameters parameters, [FromServices] IServiceProvider serviceProvider)
{
if (parameters is null)
{
throw new ArgumentNullException(nameof(parameters));
}
if (serviceProvider is null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}
return this.Ok(this.Repository.GetAll().ApplyOData(this.Request, parameters, serviceProvider));
}
Example HTTP Post requests:
URL: /odata/examples/getexamples CONTENT:
{
"raw": "http://localhost/odata/examples?%24filter%3Dname%20eq%20%27test%27"
}
{
"filter": "name eq 'test'",
"skip": "20",
"count": "true"
}