Skip to content

Commit

Permalink
start to enable query
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhg committed Aug 8, 2020
1 parent d0c5095 commit daba950
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public ProductsController(MyDataContext context)
}

[HttpGet]
// [EnableQuery]
[EnableQuery]
public IActionResult Get(CancellationToken token)
{
return Ok(_context.Products);
Expand Down
2 changes: 2 additions & 0 deletions sample/ODataRoutingSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public void ConfigureServices(IServiceCollection services)

services.AddODataFormatter();

services.AddODataQuery();

/*
services.AddOData(opt => opt.UseODataRouting(model1).
opt.UseODataRouting("v1", model2).
Expand Down
69 changes: 69 additions & 0 deletions src/Microsoft.AspNetCore.OData/Edm/EdmHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.AspNetCore.OData.Query;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder.Annotations;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNetCore.OData.Edm
{
Expand Down Expand Up @@ -317,6 +318,74 @@ public static IEnumerable<IEdmStructuralProperty> GetAutoSelectProperties(
return autoSelectProperties;
}

public static void GetPropertyAndStructuredTypeFromPath(IEnumerable<ODataPathSegment> segments,
out IEdmProperty property, out IEdmStructuredType structuredType, out string name)
{
property = null;
structuredType = null;
name = String.Empty;
string typeCast = String.Empty;
if (segments != null)
{
IEnumerable<ODataPathSegment> reverseSegments = segments.Reverse();
foreach (var segment in reverseSegments)
{
NavigationPropertySegment navigationPathSegment = segment as NavigationPropertySegment;
if (navigationPathSegment != null)
{
property = navigationPathSegment.NavigationProperty;
if (structuredType == null)
{
structuredType = navigationPathSegment.NavigationProperty.ToEntityType();
}

name = navigationPathSegment.NavigationProperty.Name + typeCast;
return;
}

PropertySegment propertyAccessPathSegment = segment as PropertySegment;
if (propertyAccessPathSegment != null)
{
property = propertyAccessPathSegment.Property;
if (structuredType == null)
{
structuredType = GetElementType(property.Type) as IEdmStructuredType;
}
name = property.Name + typeCast;
return;
}

EntitySetSegment entitySetSegment = segment as EntitySetSegment;
if (entitySetSegment != null)
{
if (structuredType == null)
{
structuredType = entitySetSegment.EntitySet.EntityType();
}
name = entitySetSegment.EntitySet.Name + typeCast;
return;
}

TypeSegment typeSegment = segment as TypeSegment;
if (typeSegment != null)
{
structuredType = GetElementType(typeSegment.EdmType.ToEdmTypeReference(false)) as IEdmStructuredType;
typeCast = "/" + structuredType;
}
}
}
}

public static IEdmType GetElementType(IEdmTypeReference edmTypeReference)
{
if (edmTypeReference.IsCollection())
{
return edmTypeReference.AsCollection().ElementType().Definition;
}

return edmTypeReference.Definition;
}

/// <summary>
/// Check whether the two are properly related types
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5966,6 +5966,17 @@
<param name="actionDescriptor">The action context, i.e. action and controller name.</param>
<param name="request">The internal request.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.GetModelBoundPageSize(Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,System.Object,System.Linq.IQueryable,Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor,Microsoft.AspNetCore.Http.HttpRequest)">
<summary>
Get the page size.
</summary>
<param name="actionExecutedContext">The response value.</param>
<param name="responseValue">The response value.</param>
<param name="singleResultCollection">The content as SingleResult.Queryable.</param>
<param name="actionDescriptor">The action context, i.e. action and controller name.</param>
<param name="request">The request.</param>
<param name="createErrorAction">A function used to generate error response.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.CreateBadRequestResult(System.String,System.Exception)">
<summary>
Create a BadRequestObjectResult.
Expand Down Expand Up @@ -6073,6 +6084,16 @@
The <see cref="T:Microsoft.AspNetCore.OData.Query.ODataQueryOptions"/> instance constructed based on the incoming request.
</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.ContainsAutoSelectExpandProperty(System.Object,System.Linq.IQueryable,Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor,Microsoft.AspNetCore.Http.HttpRequest)">
<summary>
Determine if the
</summary>
<param name="responseValue">The response value.</param>
<param name="singleResultCollection">The content as SingleResult.Queryable.</param>
<param name="actionDescriptor">The action context, i.e. action and controller name.</param>
<param name="request">The OData path.</param>
<returns></returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.GetModel(System.Type,Microsoft.AspNetCore.Http.HttpRequest,Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor)">
<summary>
Gets the EDM model for the given type and request.Override this method to customize the EDM model used for
Expand Down
158 changes: 141 additions & 17 deletions src/Microsoft.AspNetCore.OData/Query/EnableQueryAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net;
using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
Expand All @@ -20,6 +22,7 @@
using Microsoft.AspNetCore.OData.Results;
using Microsoft.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNetCore.OData.Query
{
Expand Down Expand Up @@ -140,22 +143,21 @@ private object OnActionExecuted(
ControllerActionDescriptor actionDescriptor,
HttpRequest request)
{
//if (!_querySettings.PageSize.HasValue && responseValue != null)
//{
// GetModelBoundPageSize(responseValue, singleResultCollection, actionDescriptor, modelFunction, request.Context.Path, createErrorAction);
//}

//// Apply the query if there are any query options, if there is a page size set, in the case of
//// SingleResult or in the case of $count request.
bool shouldApplyQuery = true;
// bool shouldApplyQuery = responseValue != null &&
// request.RequestUri != null &&
// (!String.IsNullOrWhiteSpace(request.RequestUri.Query) ||
// _querySettings.PageSize.HasValue ||
// _querySettings.ModelBoundPageSize.HasValue ||
// singleResultCollection != null ||
// request.IsCountRequest() ||
// ContainsAutoSelectExpandProperty(responseValue, singleResultCollection, actionDescriptor, modelFunction, request.Context.Path));
if (!_querySettings.PageSize.HasValue && responseValue != null)
{
GetModelBoundPageSize(actionExecutedContext, responseValue, singleResultCollection, actionDescriptor, request);
}

// Apply the query if there are any query options, if there is a page size set, in the case of
// SingleResult or in the case of $count request.
bool shouldApplyQuery = responseValue != null &&
request.GetEncodedUrl() != null &&
(!String.IsNullOrWhiteSpace(request.QueryString.Value) ||
_querySettings.PageSize.HasValue ||
_querySettings.ModelBoundPageSize.HasValue ||
singleResultCollection != null ||
request.IsCountRequest() ||
ContainsAutoSelectExpandProperty(responseValue, singleResultCollection, actionDescriptor, request));

object returnValue = null;
if (shouldApplyQuery)
Expand Down Expand Up @@ -197,6 +199,43 @@ private object OnActionExecuted(
return returnValue;
}

/// <summary>
/// Get the page size.
/// </summary>
/// <param name="actionExecutedContext">The response value.</param>
/// <param name="responseValue">The response value.</param>
/// <param name="singleResultCollection">The content as SingleResult.Queryable.</param>
/// <param name="actionDescriptor">The action context, i.e. action and controller name.</param>
/// <param name="request">The request.</param>
/// <param name="createErrorAction">A function used to generate error response.</param>
private void GetModelBoundPageSize(
ActionExecutedContext actionExecutedContext,
object responseValue,
IQueryable singleResultCollection,
ControllerActionDescriptor actionDescriptor,
HttpRequest request)
{
ODataQueryContext queryContext;

try
{
queryContext = GetODataQueryContext(responseValue, singleResultCollection, actionDescriptor, request);
}
catch (InvalidOperationException e)
{
actionExecutedContext.Result = CreateBadRequestResult(Error.Format(SRResources.UriQueryStringInvalid, e.Message), e);
return;
}

ModelBoundQuerySettings querySettings = EdmHelpers.GetModelBoundQuerySettings(queryContext.TargetProperty,
queryContext.TargetStructuredType,
queryContext.Model);
if (querySettings != null && querySettings.PageSize.HasValue)
{
_querySettings.ModelBoundPageSize = querySettings.PageSize;
}
}

/// <summary>
/// Create a BadRequestObjectResult.
/// </summary>
Expand Down Expand Up @@ -523,6 +562,91 @@ public virtual void ValidateQuery(HttpRequest request, ODataQueryOptions queryOp
queryOptions.Validate(_validationSettings);
}

/// <summary>
/// Determine if the
/// </summary>
/// <param name="responseValue">The response value.</param>
/// <param name="singleResultCollection">The content as SingleResult.Queryable.</param>
/// <param name="actionDescriptor">The action context, i.e. action and controller name.</param>
/// <param name="request">The OData path.</param>
/// <returns></returns>
private static bool ContainsAutoSelectExpandProperty(
object responseValue,
IQueryable singleResultCollection,
ControllerActionDescriptor actionDescriptor,
HttpRequest request)
{
Type elementClrType = GetElementType(responseValue, singleResultCollection, actionDescriptor);

IEdmModel model = GetModel(elementClrType, request, actionDescriptor);
if (model == null)
{
throw Error.InvalidOperation(SRResources.QueryGetModelMustNotReturnNull);
}
ODataPath path = request.ODataFeature().Path;
IEdmType edmType = model.GetTypeMappingCache().GetEdmType(elementClrType, model)?.Definition;
IEdmEntityType baseEntityType = edmType as IEdmEntityType;
IEdmStructuredType structuredType = edmType as IEdmStructuredType;
IEdmProperty property = null;
if (path != null)
{
string name;
EdmHelpers.GetPropertyAndStructuredTypeFromPath(path, out property, out structuredType, out name);
}

if (baseEntityType != null)
{
List<IEdmEntityType> entityTypes = new List<IEdmEntityType>();
entityTypes.Add(baseEntityType);
entityTypes.AddRange(EdmHelpers.GetAllDerivedEntityTypes(baseEntityType, model));
foreach (var entityType in entityTypes)
{
IEnumerable<IEdmNavigationProperty> navigationProperties = entityType == baseEntityType
? entityType.NavigationProperties()
: entityType.DeclaredNavigationProperties();
if (navigationProperties != null)
{
if (navigationProperties.Any(
navigationProperty =>
EdmHelpers.IsAutoExpand(navigationProperty, property, entityType, model)))
{
return true;
}
}

IEnumerable<IEdmStructuralProperty> properties = entityType == baseEntityType
? entityType.StructuralProperties()
: entityType.DeclaredStructuralProperties();
if (properties != null)
{
foreach (var edmProperty in properties)
{
if (EdmHelpers.IsAutoSelect(edmProperty, property, entityType, model))
{
return true;
}
}
}
}
}
else if (structuredType != null)
{
IEnumerable<IEdmStructuralProperty> properties = structuredType.StructuralProperties();
if (properties != null)
{
foreach (var edmProperty in properties)
{
if (EdmHelpers.IsAutoSelect(edmProperty, property, structuredType, model))
{
return true;
}
}
}
}

return false;
}

/// <summary>
/// Gets the EDM model for the given type and request.Override this method to customize the EDM model used for
/// querying.
Expand All @@ -531,7 +655,7 @@ public virtual void ValidateQuery(HttpRequest request, ODataQueryOptions queryOp
/// <param name = "request" > The request message to retrieve a model for.</param>
/// <param name = "actionDescriptor" > The action descriptor for the action being queried on.</param>
/// <returns>The EDM model for the given type and request.</returns>
public virtual IEdmModel GetModel(
public static IEdmModel GetModel(
Type elementClrType,
HttpRequest request,
ActionDescriptor actionDescriptor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.AspNetCore.OData.Abstracts;
using Microsoft.AspNetCore.OData.Query.Validator;
using Microsoft.Extensions.DependencyInjection;
using System;

Expand Down Expand Up @@ -85,7 +86,19 @@ public static IServiceCollection AddODataQuery(this IServiceCollection services/

static void AddODataQueryServices(IServiceCollection services)
{
services.AddSingleton<DefaultQuerySettings>();

// QueryValidators.
services.AddSingleton<CountQueryValidator>();
services.AddSingleton<FilterQueryValidator>();
services.AddSingleton<ODataQueryValidator>();
services.AddSingleton<OrderByQueryValidator>();
services.AddSingleton<SelectExpandQueryValidator>();
services.AddSingleton<SkipQueryValidator>();
services.AddSingleton<SkipTokenQueryValidator>();
services.AddSingleton<TopQueryValidator>();

services.AddScoped<ODataQuerySettings>();
}
}
}
Loading

0 comments on commit daba950

Please sign in to comment.