Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for custom query option implementation with a simple extension handler #617

Open
wants to merge 5 commits into
base: release-8.x
Choose a base branch
from
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
22 changes: 22 additions & 0 deletions sample/ODataRoutingSample/ExampleQueryOptionsBindingExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//-----------------------------------------------------------------------------
// <copyright file="ExampleQueryOptionsBindingExtension.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Query.Extension;
using System.Linq;

namespace ODataRoutingSample
{
public class ExampleQueryOptionsBindingExtension : IODataQueryOptionsBindingExtension

Choose a reason for hiding this comment

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

Please provide some documentation for Public classes, functions and methods.

{
public IQueryable ApplyTo(IQueryable query, ODataQueryOptions queryOptions, ODataQuerySettings querySettings)

Choose a reason for hiding this comment

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

What are these params used for ?

{
//Do something here
return query;
}
}
}
7 changes: 4 additions & 3 deletions sample/ODataRoutingSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@
using Microsoft.AspNetCore.OData;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.OData.Batch;
using Microsoft.OData;
using ODataRoutingSample.Models;
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Reflection;
using System.Linq;

namespace ODataRoutingSample
{
Expand Down Expand Up @@ -76,7 +74,10 @@ public void ConfigureServices(IServiceCollection services)
*/
.AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(5)
.AddRouteComponents(model0)
.AddRouteComponents("v1", model1)
.AddRouteComponents("v1", model1, (services) =>
{
services.AddODataQueryOptionsBindingExtension(new ExampleQueryOptionsBindingExtension());
})
.AddRouteComponents("v2{data}", model2, services => services.AddSingleton<ODataBatchHandler, DefaultODataBatchHandler>())
.AddRouteComponents("v3", model3)
.Conventions.Add(new MyConvention())
Expand Down
52 changes: 52 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6299,6 +6299,22 @@
<param name="services">The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> to add the services to.</param>
<returns>The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> so that additional calls can be chained.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.ODataServiceCollectionExtensions.AddDataQueryOptionsBindingExtension(Microsoft.Extensions.DependencyInjection.IServiceCollection,Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension)">
<summary>
Adds a <see cref="T:Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension"/> to a service collection.
</summary>
<param name="services">The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> to add the services to.</param>
<param name="extension">The <see cref="T:Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension"/> to add to the service collection.</param>
<returns>The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> so that additional calls can be chained.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.ODataServiceCollectionExtensions.AddMultipleDataQueryOptionsBindingExtension(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{System.Collections.Generic.IList{Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension}})">
<summary>
Adds multiple <see cref="T:Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension"/> to a service collection.
</summary>
<param name="services">The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> to add the services to.</param>
<param name="extensions">The <see cref="T:Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension"/> to add to the service collection.</param>
<returns>The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> so that additional calls can be chained.</returns>
</member>
<member name="T:Microsoft.AspNetCore.OData.ODataUriFunctions">
<summary>
OData UriFunctions helper.
Expand Down Expand Up @@ -9474,6 +9490,42 @@
'methodName(argTypeName1,argTypeName2,argTypeName3..)'
</summary>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension">
<summary>
This interface allows to extend the <see cref="M:Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ApplyTo(System.Object,Microsoft.AspNetCore.OData.Query.ODataQuerySettings)"/> method
and apply custom query features.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension.ApplyTo(System.Linq.IQueryable,Microsoft.AspNetCore.OData.Query.ODataQueryOptions,Microsoft.AspNetCore.OData.Query.ODataQuerySettings)">
<summary>
Apply a custom query to the given IQueryable.
</summary>
<param name="query">The original <see cref="T:System.Linq.IQueryable"/>.</param>
<param name="queryOptions">The <see cref="T:Microsoft.AspNetCore.OData.Query.ODataQueryOptions"/> object that is executing this method.</param>
<param name="querySettings">The settings to use in query composition.</param>
<returns>The new <see cref="T:System.Linq.IQueryable"/> after the query has been applied to.</returns>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Extension.MultipleODataQueryOptionsBindingExtension">
<summary>
This class allows to use multiple <see cref="T:Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension"/> extenion interfaces.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Extension.MultipleODataQueryOptionsBindingExtension.#ctor(System.Collections.Generic.IList{Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension})">
<summary>
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.OData.Query.Extension.MultipleODataQueryOptionsBindingExtension"/> class
with a list of extension interfaces.
</summary>
<param name="extensions">A list of query extensions to apply.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Extension.MultipleODataQueryOptionsBindingExtension.ApplyTo(System.Linq.IQueryable,Microsoft.AspNetCore.OData.Query.ODataQueryOptions,Microsoft.AspNetCore.OData.Query.ODataQuerySettings)">
<summary>
Apply multiple custom queries to the given IQueryable.
</summary>
<param name="query">The original <see cref="T:System.Linq.IQueryable"/>.</param>
<param name="queryOptions">The <see cref="T:Microsoft.AspNetCore.OData.Query.ODataQueryOptions"/> object that is executing this method.</param>
<param name="querySettings">The settings to use in query composition.</param>
<returns>The new <see cref="T:System.Linq.IQueryable"/> after the query has been applied to.</returns>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.HandleNullPropagationOption">
<summary>
This enum defines how to handle null propagation in queryable support.
Expand Down
50 changes: 50 additions & 0 deletions src/Microsoft.AspNetCore.OData/ODataServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Query.Extension;
using Microsoft.AspNetCore.OData.Routing;
using Microsoft.AspNetCore.OData.Routing.Parser;
using Microsoft.AspNetCore.OData.Routing.Template;
Expand Down Expand Up @@ -105,5 +108,52 @@ internal static IServiceCollection AddODataCore(this IServiceCollection services

return services;
}

/// <summary>
/// Adds a <see cref="IODataQueryOptionsBindingExtension"/> to a service collection.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="extension">The <see cref="IODataQueryOptionsBindingExtension"/> to add to the service collection.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddODataQueryOptionsBindingExtension(this IServiceCollection services, IODataQueryOptionsBindingExtension extension)
{
if (services == null)
{
throw Error.ArgumentNull(nameof(services));
}

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

services.AddSingleton<IODataQueryOptionsBindingExtension>(extension);
return services;
}

/// <summary>
/// Adds multiple <see cref="IODataQueryOptionsBindingExtension"/> to a service collection.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="extensions">The <see cref="IODataQueryOptionsBindingExtension"/> to add to the service collection.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddMultipleODataQueryOptionsBindingExtension(this IServiceCollection services, Action<IList<IODataQueryOptionsBindingExtension>> extensions)
{
if (services == null)
{
throw Error.ArgumentNull(nameof(services));
}

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

IList<IODataQueryOptionsBindingExtension> extensionResult = new List<IODataQueryOptionsBindingExtension>();
extensions.Invoke(extensionResult);
services.AddSingleton<IODataQueryOptionsBindingExtension>(new MultipleODataQueryOptionsBindingExtension(extensionResult));

return services;
}
}
}
7 changes: 7 additions & 0 deletions src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,11 @@ Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder
Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder.FilterBinder.get -> Microsoft.AspNetCore.OData.Query.Expressions.IFilterBinder
Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder.OrderByBinder.get -> Microsoft.AspNetCore.OData.Query.Expressions.IOrderByBinder
Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder.SelectExpandBinder(Microsoft.AspNetCore.OData.Query.Expressions.IFilterBinder filterBinder, Microsoft.AspNetCore.OData.Query.Expressions.IOrderByBinder orderByBinder) -> void
Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension
Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension.ApplyTo(System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) -> System.Linq.IQueryable
Microsoft.AspNetCore.OData.Query.Extension.MultipleODataQueryOptionsBindingExtension
Microsoft.AspNetCore.OData.Query.Extension.MultipleODataQueryOptionsBindingExtension.ApplyTo(System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) -> System.Linq.IQueryable
Microsoft.AspNetCore.OData.Query.Extension.MultipleODataQueryOptionsBindingExtension.MultipleODataQueryOptionsBindingExtension(System.Collections.Generic.IList<Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension> extensions) -> void
Microsoft.AspNetCore.OData.Query.FilterQueryOption
Microsoft.AspNetCore.OData.Query.FilterQueryOption.ApplyTo(System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings) -> System.Linq.IQueryable
Microsoft.AspNetCore.OData.Query.FilterQueryOption.Compute.get -> Microsoft.AspNetCore.OData.Query.ComputeQueryOption
Expand Down Expand Up @@ -1613,6 +1618,8 @@ static Microsoft.AspNetCore.OData.ODataMvcBuilderExtensions.AddOData(this Micros
static Microsoft.AspNetCore.OData.ODataMvcCoreBuilderExtensions.AddOData(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder
static Microsoft.AspNetCore.OData.ODataMvcCoreBuilderExtensions.AddOData(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.OData.ODataOptions, System.IServiceProvider> setupAction) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder
static Microsoft.AspNetCore.OData.ODataMvcCoreBuilderExtensions.AddOData(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.OData.ODataOptions> setupAction) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder
static Microsoft.AspNetCore.OData.ODataServiceCollectionExtensions.AddDataQueryOptionsBindingExtension(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension extension) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.AspNetCore.OData.ODataServiceCollectionExtensions.AddMultipleDataQueryOptionsBindingExtension(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<System.Collections.Generic.IList<Microsoft.AspNetCore.OData.Query.Extension.IODataQueryOptionsBindingExtension>> extensions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.AspNetCore.OData.ODataServiceCollectionExtensions.AddODataQueryFilter(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.AspNetCore.OData.ODataServiceCollectionExtensions.AddODataQueryFilter(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.AspNetCore.Mvc.Filters.IActionFilter queryFilter) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.AspNetCore.OData.ODataUriFunctions.AddCustomUriFunction(string functionName, Microsoft.OData.UriParser.FunctionSignatureWithReturnType functionSignature, System.Reflection.MethodInfo methodInfo) -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//-----------------------------------------------------------------------------
// <copyright file="IODataQueryOptionsBindingExtension.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System.Linq;

namespace Microsoft.AspNetCore.OData.Query.Extension
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please add some tests for this new feature?

Copy link
Author

Choose a reason for hiding this comment

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

I have added some tests.

{

/// <summary>
/// This interface allows to extend the <see cref="ODataQueryOptions.ApplyTo(object, ODataQuerySettings)"/> method
/// and apply custom query features.
/// </summary>
public interface IODataQueryOptionsBindingExtension
{

/// <summary>
/// Apply a custom query to the given IQueryable.
/// </summary>
/// <param name="query">The original <see cref="IQueryable"/>.</param>
/// <param name="queryOptions">The <see cref="ODataQueryOptions"/> object that is executing this method.</param>
/// <param name="querySettings">The settings to use in query composition.</param>
/// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns>
public IQueryable ApplyTo(IQueryable query, ODataQueryOptions queryOptions, ODataQuerySettings querySettings);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// <copyright file="MultipleODataQueryOptionsBindingExtension.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System.Collections.Generic;
using System.Linq;

namespace Microsoft.AspNetCore.OData.Query.Extension
{

/// <summary>
/// This class allows to use multiple <see cref="IODataQueryOptionsBindingExtension"/> extenion interfaces.
/// </summary>
public class MultipleODataQueryOptionsBindingExtension : IODataQueryOptionsBindingExtension
{

private readonly IList<IODataQueryOptionsBindingExtension> _extensions;

/// <summary>
/// Initializes a new instance of the <see cref="MultipleODataQueryOptionsBindingExtension"/> class
/// with a list of extension interfaces.
/// </summary>
/// <param name="extensions">A list of query extensions to apply.</param>
public MultipleODataQueryOptionsBindingExtension(IList<IODataQueryOptionsBindingExtension> extensions)
{
_extensions = extensions;
}

/// <summary>
/// Apply multiple custom queries to the given IQueryable.
/// </summary>
/// <param name="query">The original <see cref="IQueryable"/>.</param>
/// <param name="queryOptions">The <see cref="ODataQueryOptions"/> object that is executing this method.</param>
/// <param name="querySettings">The settings to use in query composition.</param>
/// <returns>The new <see cref="IQueryable"/> after the query has been applied to.</returns>
public IQueryable ApplyTo(IQueryable query, ODataQueryOptions queryOptions, ODataQuerySettings querySettings)
{
if (query == null)
{
throw Error.ArgumentNull(nameof(query));
}

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

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

IQueryable result = query;
foreach (var extension in _extensions)
{
result = extension.ApplyTo(query, queryOptions, querySettings);
}
return result;

Choose a reason for hiding this comment

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

Is the intention here to return the last result and ignore the rest ?

}
}
}
Loading