Skip to content

Instantly share code, notes, and snippets.

@dfch
Last active August 23, 2022 15:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save dfch/12697689ed0ed1c855828acb85294342 to your computer and use it in GitHub Desktop.
Save dfch/12697689ed0ed1c855828acb85294342 to your computer and use it in GitHub Desktop.
ODataQueryOptionExtensions - Modifying ODataQueryOptions on the fly - https://d-fens.ch/2017/02/26/modifying-odataqueryoptions-on-the-fly/
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Web.Http.OData.Query;
namespace Net.Appclusive.WebApi.OdataServices
{
public static class ODataQueryOptionsExtensions
{
public static readonly ODataQuerySettings _OdataQuerySettings = new ODataQuerySettings();
public static ODataQueryOptions Transform
(
this ODataQueryOptions queryOptions,
ODataQueryOptionsUtilitiesTransformSettings transform
)
{
return ODataQueryOptionsUtilities.Transform
(
queryOptions,
transform
);
}
public static ODataQueryOptions Transform
(
this ODataQueryOptions queryOptions,
Func<IReadOnlyDictionary<string, string>, FilterQueryOption, string> transformFilter,
Func<IReadOnlyDictionary<string, string>, OrderByQueryOption, IList<string>> transformOrderBy,
Func<IReadOnlyDictionary<string, string>, SelectExpandQueryOption, IList<string>> transformExpand,
Func<IReadOnlyDictionary<string, string>, SelectExpandQueryOption, IList<string>> transformSelect,
Func<IReadOnlyDictionary<string, string>, SkipQueryOption, int?> transformSkip,
Func<IReadOnlyDictionary<string, string>, TopQueryOption, int?> transformTop
)
{
Contract.Requires(null != queryOptions);
return ODataQueryOptionsUtilities.Transform
(
queryOptions: queryOptions,
transformFilter: transformFilter,
transformOrderBy: transformOrderBy,
transformExpand: transformExpand,
transformSelect: transformSelect,
transformSkip: transformSkip,
transformTop: transformTop
);
}
public static ODataQueryOptions RemoveSkipCount(this ODataQueryOptions queryOptions)
{
Contract.Requires(null != queryOptions);
Contract.Ensures(null != Contract.Result<ODataQueryOptions>());
return ODataQueryOptionsUtilities.Transform
(
queryOptions,
new ODataQueryOptionsUtilitiesTransformSettings
{
Skip = (map, option) => default(int?)
}
);
}
public static ODataQueryOptions SetSkipCount(this ODataQueryOptions queryOptions, int value)
{
Contract.Requires(null != queryOptions);
Contract.Requires(0 <= value);
Contract.Ensures(null != Contract.Result<ODataQueryOptions>());
return ODataQueryOptionsUtilities.Transform
(
queryOptions,
new ODataQueryOptionsUtilitiesTransformSettings
{
Skip = (map, option) => value
}
);
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Web.Http.OData.Query;
using biz.dfch.CS.Commons.Linq;
using Net.Appclusive.Public.Domain;
namespace Net.Appclusive.WebApi.OdataServices
{
public class ODataQueryOptionsUtilities
{
public const char QUERY_OPTIONS_PREFIX = '?';
public const char QUERY_OPTIONS_SEPARATOR = '&';
public const char QUERY_OPTIONS_DELIMITER = '=';
public const string QUERY_OPTION_EXPAND = "$expand";
public const string QUERY_OPTION_SELECT = "$select";
public const string QUERY_OPTION_SKIP = "$skip";
public const string QUERY_OPTION_TOP = "$top";
public const char QUERY_OPTION_SELECT_EXPAND_DELIMITER = ',';
public static ODataQueryOptions Transform
(
ODataQueryOptions queryOptions,
ODataQueryOptionsUtilitiesTransformSettings settings
)
{
Contract.Requires(null != queryOptions);
Contract.Requires(null != settings);
Contract.Ensures(null != Contract.Result<ODataQueryOptions>());
return Transform
(
queryOptions: queryOptions,
transformFilter: settings.Filter,
transformOrderBy: settings.OrderBy,
transformExpand: settings.Expand,
transformSelect: settings.Select,
transformSkip: settings.Skip,
transformTop: settings.Top
);
}
public static ODataQueryOptions Transform
(
ODataQueryOptions queryOptions,
Func<IReadOnlyDictionary<string, string>, FilterQueryOption, string> transformFilter,
Func<IReadOnlyDictionary<string, string>, OrderByQueryOption, IList<string>> transformOrderBy,
Func<IReadOnlyDictionary<string, string>, SelectExpandQueryOption, IList<string>> transformExpand,
Func<IReadOnlyDictionary<string, string>, SelectExpandQueryOption, IList<string>> transformSelect,
Func<IReadOnlyDictionary<string, string>, SkipQueryOption, int?> transformSkip,
Func<IReadOnlyDictionary<string, string>, TopQueryOption, int?> transformTop
)
{
Contract.Requires(null != queryOptions);
Contract.Ensures(null != Contract.Result<ODataQueryOptions>());
var uri = queryOptions.Request.RequestUri;
var queryParameters = uri.Query
.TrimStart(QUERY_OPTIONS_PREFIX)
.Split(new[] { QUERY_OPTIONS_SEPARATOR }, StringSplitOptions.RemoveEmptyEntries)
.ToDictionary
(
e => e.Split(QUERY_OPTIONS_DELIMITER).FirstOrDefault(),
e => e.Split(QUERY_OPTIONS_DELIMITER).LastOrDefault()
);
if (null != transformFilter)
{
var result = transformFilter(new ReadOnlyDictionary<string, string>(queryParameters), queryOptions.Filter);
if (null != result)
{
queryParameters[QUERY_OPTION_EXPAND] = result;
}
}
if (null != transformOrderBy)
{
var result = transformOrderBy(new ReadOnlyDictionary<string, string>(queryParameters), queryOptions.OrderBy);
if (null != result)
{
queryParameters[QUERY_OPTION_EXPAND] = string.Join(QUERY_OPTION_SELECT_EXPAND_DELIMITER.ToString(), result);
}
}
if (null != transformExpand)
{
var result = transformExpand(new ReadOnlyDictionary<string, string>(queryParameters), queryOptions.SelectExpand);
if (null != result)
{
queryParameters[QUERY_OPTION_EXPAND] = string.Join(QUERY_OPTION_SELECT_EXPAND_DELIMITER.ToString(), result);
}
}
if (null != transformSelect)
{
var result = transformSelect(new ReadOnlyDictionary<string, string>(queryParameters), queryOptions.SelectExpand);
if (null != result)
{
queryParameters[QUERY_OPTION_SELECT] = string.Join(QUERY_OPTION_SELECT_EXPAND_DELIMITER.ToString(), result);
}
}
if (null != transformSkip)
{
var result = transformSkip(new ReadOnlyDictionary<string, string>(queryParameters), queryOptions.Skip);
if (result.HasValue)
{
queryParameters[QUERY_OPTION_SKIP] = result.Value.ToString();
}
else
{
if (queryParameters.ContainsKey(QUERY_OPTION_SKIP))
{
queryParameters.Remove(QUERY_OPTION_SKIP);
}
}
}
if (null != transformTop)
{
var result = transformTop(new ReadOnlyDictionary<string, string>(queryParameters), queryOptions.Top);
if (result.HasValue)
{
queryParameters[QUERY_OPTION_TOP] = result.Value.ToString();
}
else
{
if (queryParameters.ContainsKey(QUERY_OPTION_TOP))
{
queryParameters.Remove(QUERY_OPTION_TOP);
}
}
}
var newQueryOptions = new StringBuilder();
queryParameters.ForEach(e =>
{
newQueryOptions.Append(QUERY_OPTIONS_SEPARATOR);
newQueryOptions.Append(e.Key);
newQueryOptions.Append(QUERY_OPTIONS_DELIMITER);
newQueryOptions.Append(e.Value);
});
queryOptions.Request.RequestUri =
new Uri($"{uri.Scheme}://{uri.Authority}{uri.AbsolutePath}{QUERY_OPTIONS_PREFIX}{newQueryOptions}");
var newOdataQueryOptions = (ODataQueryOptions)Activator.CreateInstance(queryOptions.GetType(), queryOptions.Context, queryOptions.Request);
return newOdataQueryOptions;
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Web.Http.OData.Query;
namespace Net.Appclusive.WebApi.OdataServices
{
public class ODataQueryOptionsUtilitiesTransformSettings
{
public Func<IReadOnlyDictionary<string, string>, FilterQueryOption, string> Filter;
public Func<IReadOnlyDictionary<string, string>, OrderByQueryOption, IList<string>> OrderBy;
public Func<IReadOnlyDictionary<string, string>, SelectExpandQueryOption, IList<string>> Expand;
public Func<IReadOnlyDictionary<string, string>, SelectExpandQueryOption, IList<string>> Select;
public Func<IReadOnlyDictionary<string, string>, SkipQueryOption, int?> Skip;
public Func<IReadOnlyDictionary<string, string>, TopQueryOption, int?> Top;
}
}
@victorperez2911
Copy link

@dfch

https://gist.github.com/victorperez2911/a23989aa73263f5b285e9a0961d90260

  1. $filter fix and
  2. oData v4 NameSpace references.
  3. Generics parameter on ODataQueryOptions.

Victor Perez

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment