Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11729,6 +11729,16 @@
<param name="context">The <see cref="T:Microsoft.AspNetCore.OData.Query.ODataQueryContext"/> which contains the <see cref="T:Microsoft.OData.Edm.IEdmModel"/> and some type information.</param>
<param name="request">The incoming request message.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.ODataQueryOptions.#ctor(System.Collections.Generic.IDictionary{System.String,System.String},System.Type,Microsoft.OData.Edm.IEdmModel,Microsoft.OData.UriParser.ODataPath)">
<summary>
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.OData.Query.ODataQueryOptions"/> class based on the given query parameters and context.
</summary>
<param name="queryParameters">The OData query parameters as a dictionary.</param>
<param name="model">The EdmModel that includes the <see cref="T:Microsoft.OData.Edm.IEdmType"/> corresponding to
the given <paramref name="elementClrType"/>.</param>
<param name="elementClrType">The CLR type of the element of the collection being queried.</param>
<param name="path">The parsed <see cref="T:Microsoft.OData.UriParser.ODataPath"/>.</param>
</member>
<member name="P:Microsoft.AspNetCore.OData.Query.ODataQueryOptions.Request">
<summary>
Gets the request message associated with this instance.
Expand Down Expand Up @@ -11958,6 +11968,14 @@
<param name="request">The incoming request message</param>
<remarks>This signature uses types that are AspNetCore-specific.</remarks>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.ODataQueryOptions`1.#ctor(System.Collections.Generic.IDictionary{System.String,System.String},Microsoft.OData.Edm.IEdmModel,Microsoft.OData.UriParser.ODataPath)">
<summary>
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.OData.Query.ODataQueryOptions`1"/> class based on the given query parameters and context.
</summary>
<param name="model">The EDM model (can be null for non-model scenarios).</param>
<param name="queryParameters">The OData query parameters as a dictionary.</param>
<param name="path">The ODataPath (optional, can be null).</param>
</member>
<member name="P:Microsoft.AspNetCore.OData.Query.ODataQueryOptions`1.IfMatch">
<summary>
Gets the <see cref="T:Microsoft.AspNetCore.OData.Query.ETag`1"/> from IfMatch header.
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter
Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.ODataQueryEndpointFilter() -> void
Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.QuerySettings.get -> Microsoft.AspNetCore.OData.Query.ODataQuerySettings
Microsoft.AspNetCore.OData.Query.ODataQueryEndpointFilter.ValidationSettings.get -> Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings
Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ODataQueryOptions(System.Collections.Generic.IDictionary<string, string> queryParameters, System.Type elementClrType, Microsoft.OData.Edm.IEdmModel model = null, Microsoft.OData.UriParser.ODataPath path = null) -> void
Microsoft.AspNetCore.OData.Query.ODataQueryOptions<TEntity>.ODataQueryOptions(System.Collections.Generic.IDictionary<string, string> queryParameters, Microsoft.OData.Edm.IEdmModel model = null, Microsoft.OData.UriParser.ODataPath path = null) -> void
Microsoft.AspNetCore.OData.Query.Wrapper.SelectExpandWrapperConverter
Microsoft.AspNetCore.OData.Query.Wrapper.SelectExpandWrapperConverter.SelectExpandWrapperConverter() -> void
Microsoft.AspNetCore.OData.Results.IODataResult
Expand Down
137 changes: 136 additions & 1 deletion src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,66 @@ public ODataQueryOptions(ODataQueryContext context, HttpRequest request)
Initialize(context);
}

/// <summary>
/// Initializes a new instance of the <see cref="ODataQueryOptions"/> class based on the given query parameters and context.
/// </summary>
/// <param name="queryParameters">The OData query parameters as a dictionary.</param>
/// <param name="model">The EdmModel that includes the <see cref="IEdmType"/> corresponding to
/// the given <paramref name="elementClrType"/>.</param>
/// <param name="elementClrType">The CLR type of the element of the collection being queried.</param>
/// <param name="path">The parsed <see cref="ODataPath"/>.</param>
public ODataQueryOptions(IDictionary<string, string> queryParameters, Type elementClrType, IEdmModel model = null, ODataPath path = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion is to accept

  1. IserviceProvider or
  2. other required services?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which services are required by ODataQueryOptions?

{
if (queryParameters == null)
{
throw Error.ArgumentNull(nameof(queryParameters));
}

if (elementClrType == null)
{
throw Error.ArgumentNull(nameof(elementClrType));
}

if (model == null)
{
RawValues = new ODataRawQueryOptions();

BuildQueryOptionsOnly(queryParameters);
return;
}

ODataQueryContext context = new ODataQueryContext(model ?? EdmCoreModel.Instance, elementClrType, path);

Context = context;
Request = context.Request;

RawValues = new ODataRawQueryOptions();

// Use the provided dictionary directly
_queryOptionParser = new ODataQueryOptionParser(
context.Model,
context.ElementType,
context.NavigationSource,
queryParameters,
context.RequestContainer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where do you get the RequestContainer? you use 'context.RequestContainer', is it valid?


var uriResolver = context.RequestContainer?.GetService<ODataUriResolver>();
if (uriResolver != null)
{
_queryOptionParser.Resolver = uriResolver;
_enableNoDollarSignQueryOptions = uriResolver.EnableNoDollarQueryOptions;
}
else
{
_queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver;
_enableNoDollarSignQueryOptions = false;
}

BuildQueryOptions(queryParameters);

Validator = context.GetODataQueryValidator();
}

/// <summary>
/// Gets the request message associated with this instance.
/// </summary>
Expand Down Expand Up @@ -1052,7 +1112,8 @@ private void BuildQueryOptions(IDictionary<string, string> queryParameters)
Context, _queryOptionParser);
}

if (Request.IsCountRequest())
// Ensure Request is not null before checking for CountRequest
if (Request != null && Request.IsCountRequest())
{
Count = new CountQueryOption(
"true",
Expand All @@ -1066,6 +1127,80 @@ private void BuildQueryOptions(IDictionary<string, string> queryParameters)
}
}

private void BuildQueryOptionsOnly(IDictionary<string, string> queryParameters)
{
foreach (KeyValuePair<string, string> kvp in queryParameters)
{
switch (kvp.Key.ToLowerInvariant())
{
case "$filter":
ThrowIfEmpty(kvp.Value, "$filter");
RawValues.Filter = kvp.Value;
Filter = new FilterQueryOption(kvp.Value);
break;
case "$orderby":
ThrowIfEmpty(kvp.Value, "$orderby");
RawValues.OrderBy = kvp.Value;
OrderBy = new OrderByQueryOption(kvp.Value);
break;
case "$top":
ThrowIfEmpty(kvp.Value, "$top");
RawValues.Top = kvp.Value;
Top = new TopQueryOption(kvp.Value);
break;
case "$skip":
ThrowIfEmpty(kvp.Value, "$skip");
RawValues.Skip = kvp.Value;
Skip = new SkipQueryOption(kvp.Value);
break;
case "$select":
RawValues.Select = kvp.Value;
break;
case "$count":
ThrowIfEmpty(kvp.Value, "$count");
RawValues.Count = kvp.Value;
Count = new CountQueryOption(kvp.Value);
break;
case "$expand":
RawValues.Expand = kvp.Value;
break;
case "$format":
RawValues.Format = kvp.Value;
break;
case "$skiptoken":
RawValues.SkipToken = kvp.Value;
SkipToken = new SkipTokenQueryOption(kvp.Value);
break;
case "$deltatoken":
RawValues.DeltaToken = kvp.Value;
break;
case "$apply":
ThrowIfEmpty(kvp.Value, "$apply");
RawValues.Apply = kvp.Value;
Apply = new ApplyQueryOption(kvp.Value);
break;
case "$compute":
ThrowIfEmpty(kvp.Value, "$compute");
RawValues.Compute = kvp.Value;
Compute = new ComputeQueryOption(kvp.Value);
break;
case "$search":
ThrowIfEmpty(kvp.Value, "$search");
RawValues.Search = kvp.Value;
Search = new SearchQueryOption(kvp.Value);
break;
default:
// we don't throw if we can't recognize the query
break;
}
}

if (!string.IsNullOrWhiteSpace(RawValues.Select) || !string.IsNullOrWhiteSpace(RawValues.Expand))
{
SelectExpand = new SelectExpandQueryOption(RawValues.Select, RawValues.Expand);
}
}

private static bool IsAvailableODataQueryOption(object queryOption, ODataQuerySettings querySettings, AllowedQueryOptions queryOptionFlag)
{
return (queryOption != null) &&
Expand Down
12 changes: 12 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/ODataQueryOptionsOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
Expand Down Expand Up @@ -48,6 +49,17 @@ public ODataQueryOptions(ODataQueryContext context, HttpRequest request)
}
}

/// <summary>
/// Initializes a new instance of the <see cref="ODataQueryOptions{TEntity}"/> class based on the given query parameters and context.
/// </summary>
/// <param name="model">The EDM model (can be null for non-model scenarios).</param>
/// <param name="queryParameters">The OData query parameters as a dictionary.</param>
/// <param name="path">The ODataPath (optional, can be null).</param>
public ODataQueryOptions(IDictionary<string, string> queryParameters, IEdmModel model = null, ODataPath path = null)
: base(queryParameters, typeof(TEntity), model, path)
{
}

/// <summary>
/// Gets the <see cref="ETag{TEntity}"/> from IfMatch header.
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/ApplyQueryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ public ApplyQueryOption(string rawValue, ODataQueryContext context, ODataQueryOp
ResultClrType = Context.ElementClrType;
}

// Used when only the raw value is known.
internal ApplyQueryOption(string rawValue)
{
if (string.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty(nameof(rawValue));
}

RawValue = rawValue;
}

// for unit test only
internal ApplyQueryOption(string rawValue, ODataQueryContext context)
{
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ public ComputeQueryOption(string rawValue, ODataQueryContext context, ODataQuery
ResultClrType = Context.ElementClrType;
}

// Used when only the raw value is known.
internal ComputeQueryOption(string rawValue)
{
if (string.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty(nameof(rawValue));
}

RawValue = rawValue;
}

// This constructor is intended for unit testing only.
internal ComputeQueryOption(string rawValue, ODataQueryContext context)
{
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ public CountQueryOption(string rawValue, ODataQueryContext context, ODataQueryOp
_queryOptionParser = queryOptionParser;
}

// Used when only the raw value is known.
internal CountQueryOption(string rawValue)
{
if (String.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty("rawValue");
}

RawValue = rawValue;
}

// This constructor is intended for unit testing only.
internal CountQueryOption(string rawValue, ODataQueryContext context)
{
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ public FilterQueryOption(string rawValue, ODataQueryContext context, ODataQueryO
_queryOptionParser = queryOptionParser;
}

// Used when only the raw value is known.
internal FilterQueryOption(string rawValue)
{
if (String.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty("rawValue");
}

RawValue = rawValue;
}

internal FilterQueryOption(ODataQueryContext context, FilterClause filterClause)
{
_filterClause = filterClause;
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ public OrderByQueryOption(string rawValue, ODataQueryContext context, ODataQuery
_queryOptionParser = queryOptionParser;
}

// Used when only the raw value is known.
internal OrderByQueryOption(string rawValue)
{
if (String.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty("rawValue");
}

RawValue = rawValue;
}

internal OrderByQueryOption(string rawValue, ODataQueryContext context, string applyRaw, string computeRaw)
{
if (context == null)
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ public SearchQueryOption(string rawValue, ODataQueryContext context, ODataQueryO
ResultClrType = Context.ElementClrType;
}

// Used when only the raw value is known.
internal SearchQueryOption(string rawValue)
{
if (string.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty(nameof(rawValue));
}

RawValue = rawValue;
}

// This constructor is intended for unit testing only.
internal SearchQueryOption(string rawValue, ODataQueryContext context)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ public SelectExpandQueryOption(string select, string expand, ODataQueryContext c
_queryOptionParser = queryOptionParser;
}

internal SelectExpandQueryOption(string select, string expand)
{
if (string.IsNullOrWhiteSpace(select) && string.IsNullOrWhiteSpace(expand))
{
throw Error.Argument(SRResources.SelectExpandEmptyOrNull);
}

RawSelect = select;
RawExpand = expand;
}

internal SelectExpandQueryOption(
string select,
string expand,
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/SkipQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ public SkipQueryOption(string rawValue, ODataQueryContext context, ODataQueryOpt
_queryOptionParser = queryOptionParser;
}

// Used when only the raw value is known.
internal SkipQueryOption(string rawValue)
{
if (String.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty(nameof(rawValue));
}

RawValue = rawValue;
}

// This constructor is intended for unit testing only.
internal SkipQueryOption(string rawValue, ODataQueryContext context)
{
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/SkipTokenQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ public SkipTokenQueryOption(string rawValue, ODataQueryContext context)
Context = context;
}

// Used when only the raw value is known.
internal SkipTokenQueryOption(string rawValue)
{
if (string.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty(nameof(rawValue));
}

RawValue = rawValue;
}

/// <summary>
/// Gets the raw $skiptoken value.
/// </summary>
Expand Down
Loading