From 4a54f9a28c4c27decf283a99936edac4b146d3f8 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Mon, 8 Aug 2022 14:25:30 -0700 Subject: [PATCH 1/2] add omit-values supports --- .../Extensions/HttpRequestExtensions.cs | 39 ++++++ .../Formatter/ODataOutputFormatter.cs | 3 + .../Formatter/ODataOutputFormatterHelper.cs | 3 +- .../Formatter/OmitValuesKind.cs | 33 +++++ .../Formatter/ResponsePreferenceHelpers.cs | 55 ++++++++ .../Serialization/ODataResourceSerializer.cs | 118 +++++++++++++++--- .../Serialization/ODataSerializerContext.cs | 6 + .../Microsoft.AspNetCore.OData.xml | 91 +++++++++++--- .../ODataOptions.cs | 6 + .../PublicAPI.Unshipped.txt | 11 ++ 10 files changed, 329 insertions(+), 36 deletions(-) create mode 100644 src/Microsoft.AspNetCore.OData/Formatter/OmitValuesKind.cs create mode 100644 src/Microsoft.AspNetCore.OData/Formatter/ResponsePreferenceHelpers.cs diff --git a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs index 83beb1bc1..2b6c2a23c 100644 --- a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.OData.Abstracts; using Microsoft.AspNetCore.OData.Common; +using Microsoft.AspNetCore.OData.Formatter; using Microsoft.AspNetCore.OData.Formatter.Deserialization; using Microsoft.AspNetCore.OData.Query; using Microsoft.Extensions.DependencyInjection; @@ -71,6 +72,44 @@ public static ODataOptions ODataOptions(this HttpRequest request) return request.HttpContext.ODataOptions(); } + /// + /// Returns the from the request. + /// + /// The instance to access. + /// The from the request. + public static OmitValuesKind GetOmitValuesKind(this HttpRequest request) + { + if (request == null) + { + throw Error.ArgumentNull(nameof(request)); + } + + // The 'Prefer' header from client has the top priority + string preferHeader = RequestPreferenceHelpers.GetRequestPreferHeader(request.Headers); + if (preferHeader != null) + { + // simply use the string comparision case-insensitive + if (preferHeader.Contains("omit-values=nulls", StringComparison.OrdinalIgnoreCase)) + { + return OmitValuesKind.Nulls; + } + + if (preferHeader.Contains("omit-values=defaults", StringComparison.OrdinalIgnoreCase)) + { + return OmitValuesKind.Defaults; + } + } + + // If there's no setting from client, we move to use the configuration on the OData options. + ODataOptions options = request.ODataOptions(); + if (options == null) + { + return OmitValuesKind.Unknown; + } + + return options.OmitValuesKind; + } + /// /// Gets the from the request container. /// diff --git a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs index d9255bfed..9ca27dae5 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs @@ -209,6 +209,9 @@ public override void WriteResponseHeaders(OutputFormatterWriteContext context) // Add version header. response.Headers["OData-Version"] = ODataUtils.ODataVersionToString(request.GetODataVersion()); + + // Set the prefer-applied header on response + ResponsePreferenceHelpers.SetResponsePreferAppliedHeader(response); } /// diff --git a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterHelper.cs b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterHelper.cs index 57d28ab60..787071ff7 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterHelper.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterHelper.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Runtime.Serialization; @@ -24,7 +23,6 @@ using Microsoft.Net.Http.Headers; using Microsoft.OData; using Microsoft.OData.Edm; -using Microsoft.OData.ModelBuilder; using Microsoft.OData.UriParser; namespace Microsoft.AspNetCore.OData.Formatter @@ -142,6 +140,7 @@ internal static async Task WriteToStreamAsync( writeContext.MetadataLevel = metadataLevel; writeContext.QueryOptions = queryOptions; writeContext.SetComputedProperties(queryOptions?.Compute?.ComputeClause); + writeContext.OmitValuesKind = request.GetOmitValuesKind(); //Set the SelectExpandClause on the context if it was explicitly specified. if (selectExpandDifferentFromQueryOptions != null) diff --git a/src/Microsoft.AspNetCore.OData/Formatter/OmitValuesKind.cs b/src/Microsoft.AspNetCore.OData/Formatter/OmitValuesKind.cs new file mode 100644 index 000000000..af92407c3 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Formatter/OmitValuesKind.cs @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.AspNetCore.OData.Formatter +{ + /// + /// Omit-Values kind + /// + public enum OmitValuesKind + { + /// + /// Not set, unknown + /// + Unknown, + + /// + /// If nulls is specified, then the service MAY omit properties containing null values from the response, + /// in which case it MUST specify the Preference-Applied response header with omit-values=nulls. + /// + Nulls, + + /// + /// If defaults is specified, then the service MAY omit properties containing default values from the response, including nulls for properties that have no other defined default value. + /// Nulls MUST be included for properties that have a non-null default value defined. + /// If the service omits default values it MUST specify the Preference-Applied response header with omit-values=defaults. + /// + Defaults + } +} diff --git a/src/Microsoft.AspNetCore.OData/Formatter/ResponsePreferenceHelpers.cs b/src/Microsoft.AspNetCore.OData/Formatter/ResponsePreferenceHelpers.cs new file mode 100644 index 000000000..e05f8d5a8 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Formatter/ResponsePreferenceHelpers.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.OData.Extensions; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.AspNetCore.OData.Formatter +{ + internal static class ResponsePreferenceHelpers + { + public const string PreferAppliedHeaderName = "Preference-Applied"; + + public static void SetResponsePreferAppliedHeader(HttpResponse response) + { + if (response == null) + { + throw Error.ArgumentNull(nameof(response)); + } + + HttpRequest request = response.HttpContext.Request; + + OmitValuesKind omitValuesKind = request.GetOmitValuesKind(); + if (omitValuesKind == OmitValuesKind.Unknown) + { + return; + } + + string prefer_applied = null; + if (response.Headers.TryGetValue(PreferAppliedHeaderName, out StringValues values)) + { + // If there are many "Preference-Applied" headers, pick up the first one. + prefer_applied = values.FirstOrDefault(); + } + + string omitValuesHead = omitValuesKind == OmitValuesKind.Nulls ? + "omit-values=nulls" : + "omit-values=defaults"; + + if (prefer_applied == null) + { + response.Headers[PreferAppliedHeaderName] = omitValuesHead; + } + else + { + response.Headers[PreferAppliedHeaderName] = $"{prefer_applied},{omitValuesHead}"; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs index 297e152e4..c8e4ca79a 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs @@ -749,16 +749,16 @@ private async Task WriteDynamicComplexPropertiesAsync(ResourceContext resourceCo if (edmTypeReference.IsStructured() || (edmTypeReference.IsCollection() && edmTypeReference.AsCollection().ElementType().IsStructured())) { - ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo - { - IsCollection = edmTypeReference.IsCollection(), - Name = dynamicComplexProperty.Key, - }; + ODataNestedResourceInfo nestedResourceInfo + = CreateDynamicComplexNestedResourceInfo(dynamicComplexProperty.Key, dynamicComplexProperty.Value, edmTypeReference, resourceContext); - await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); - await WriteDynamicComplexPropertyAsync(dynamicComplexProperty.Value, edmTypeReference, resourceContext, writer) - .ConfigureAwait(false); - await writer.WriteEndAsync().ConfigureAwait(false); + if (nestedResourceInfo != null) + { + await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); + await WriteDynamicComplexPropertyAsync(dynamicComplexProperty.Value, edmTypeReference, resourceContext, writer) + .ConfigureAwait(false); + await writer.WriteEndAsync().ConfigureAwait(false); + } } } } @@ -834,16 +834,14 @@ private async Task WriteComplexPropertiesAsync(SelectExpandNode selectExpandNode { IEdmStructuralProperty complexProperty = selectedComplex.Key; - ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo + ODataNestedResourceInfo nestedResourceInfo = CreateComplexNestedResourceInfo(complexProperty, selectedComplex.Value, resourceContext); + if (nestedResourceInfo != null) { - IsCollection = complexProperty.Type.IsCollection(), - Name = complexProperty.Name - }; - - await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); - await WriteComplexAndExpandedNavigationPropertyAsync(complexProperty, selectedComplex.Value, resourceContext, writer) - .ConfigureAwait(false); - await writer.WriteEndAsync().ConfigureAwait(false); + await writer.WriteStartAsync(nestedResourceInfo).ConfigureAwait(false); + await WriteComplexAndExpandedNavigationPropertyAsync(complexProperty, selectedComplex.Value, resourceContext, writer) + .ConfigureAwait(false); + await writer.WriteEndAsync().ConfigureAwait(false); + } } } @@ -960,6 +958,66 @@ private IEnumerable CreateNavigationLinks( } } + /// + /// Creates the to be written while writing this dynamic complex property. + /// + /// The dynamic property name. + /// The dynamic property value. + /// The edm type reference. + /// The context for the complex instance being written. + /// The nested resource info to be written. Returns 'null' will omit this serialization. + /// It enables customer to get more controll by overriding this method. + public virtual ODataNestedResourceInfo CreateDynamicComplexNestedResourceInfo(string propertyName, object propertyValue, IEdmTypeReference edmType, ResourceContext resourceContext) + { + if (edmType == null) + { + throw Error.ArgumentNull(nameof(edmType)); + } + + return new ODataNestedResourceInfo + { + IsCollection = edmType.IsCollection(), + Name = propertyName, + }; + } + + /// + /// Creates the to be written while writing this complex property. + /// + /// The complex property for which the nested resource info is being created. + /// The corresponding sub select item belongs to this complex property. + /// The context for the complex instance being written. + /// The nested resource info to be written. Returns 'null' will omit this complex serialization. + /// It enables customer to get more controll by overriding this method. + public virtual ODataNestedResourceInfo CreateComplexNestedResourceInfo(IEdmStructuralProperty complexProperty, PathSelectItem pathSelectItem, ResourceContext resourceContext) + { + if (complexProperty == null) + { + throw Error.ArgumentNull(nameof(complexProperty)); + } + + if (resourceContext == null) + { + throw Error.ArgumentNull(nameof(resourceContext)); + } + + + if (resourceContext.SerializerContext.OmitValuesKind == OmitValuesKind.Nulls) + { + object propertyValue = resourceContext.GetPropertyValue(complexProperty.Name); + if (propertyValue == null || propertyValue is NullEdmComplexObject) + { + return null; + } + } + + return new ODataNestedResourceInfo + { + IsCollection = complexProperty.Type.IsCollection(), + Name = complexProperty.Name + }; + } + /// /// Creates the to be written while writing this entity. /// @@ -978,6 +1036,15 @@ public virtual ODataNestedResourceInfo CreateNavigationLink(IEdmNavigationProper throw Error.ArgumentNull(nameof(resourceContext)); } + if (resourceContext.SerializerContext.OmitValuesKind == OmitValuesKind.Nulls) + { + object propertyValue = resourceContext.GetPropertyValue(navigationProperty.Name); + if (propertyValue == null) + { + return null; + } + } + ODataSerializerContext writeContext = resourceContext.SerializerContext; IEdmNavigationSource navigationSource = writeContext.NavigationSource; ODataNestedResourceInfo navigationLink = null; @@ -1160,6 +1227,21 @@ public virtual ODataProperty CreateStructuralProperty(IEdmStructuralProperty str object propertyValue = resourceContext.GetPropertyValue(structuralProperty.Name); + if (resourceContext.SerializerContext.OmitValuesKind == OmitValuesKind.Nulls && propertyValue == null) + { + return null; // omit null value + } + + if (resourceContext.SerializerContext.OmitValuesKind == OmitValuesKind.Defaults && + structuralProperty.DefaultValueString != null && + structuralProperty.Type.IsString() && propertyValue != null) // let's focus on the "Edm.String" property only, NOW!! + { + if (propertyValue.ToString() == structuralProperty.DefaultValueString) + { + return null; // omit default value + } + } + IEdmTypeReference propertyType = structuralProperty.Type; if (propertyValue != null) { diff --git a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataSerializerContext.cs b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataSerializerContext.cs index 725a1d525..d132be239 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataSerializerContext.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataSerializerContext.cs @@ -78,6 +78,7 @@ internal ODataSerializerContext(ResourceContext resource, IEdmProperty edmProper Items = context.Items; ExpandReference = context.ExpandReference; TimeZone = context.TimeZone; + OmitValuesKind = context.OmitValuesKind; QueryContext = queryContext; @@ -180,6 +181,11 @@ internal ODataSerializerContext(ResourceContext resource, IEdmProperty edmProper /// public TimeZoneInfo TimeZone { get; set; } + /// + /// Gets or sets the . + /// + public OmitValuesKind OmitValuesKind { get; set; } + /// /// Gets or sets the . /// diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index dc0971029..ce01039e7 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -1094,6 +1094,20 @@ The type to test. True if the type is a DateTime; false otherwise. + + + Determine if a type is a . + + The type to test. + True if the type is a DateOnly; false otherwise. + + + + Determine if a type is a . + + The type to test. + True if the type is a TimeOnly; false otherwise. + Determine if a type is a TimeSpan. @@ -2894,6 +2908,13 @@ The instance to extend. The instance from the DI container. + + + Returns the from the request. + + The instance to access. + The from the request. + Gets the from the request container. @@ -4076,6 +4097,29 @@ Gets the OData action of this parameters. + + + Omit-Values kind + + + + + Not set, unknown + + + + + If nulls is specified, then the service MAY omit properties containing null values from the response, + in which case it MUST specify the Preference-Applied response header with omit-values=nulls. + + + + + If defaults is specified, then the service MAY omit properties containing default values from the response, including nulls for properties that have no other defined default value. + Nulls MUST be included for properties that have a non-null default value defined. + If the service omits default values it MUST specify the Preference-Applied response header with omit-values=defaults. + + Contains context information about the resource currently being serialized. @@ -4606,6 +4650,27 @@ Write the navigation link for the select navigation properties. + + + Creates the to be written while writing this dynamic complex property. + + The dynamic property name. + The dynamic property value. + The edm type reference. + The context for the complex instance being written. + The nested resource info to be written. Returns 'null' will omit this serialization. + It enables customer to get more controll by overriding this method. + + + + Creates the to be written while writing this complex property. + + The complex property for which the nested resource info is being created. + The corresponding sub select item belongs to this complex property. + The context for the complex instance being written. + The nested resource info to be written. Returns 'null' will omit this complex serialization. + It enables customer to get more controll by overriding this method. + Creates the to be written while writing this entity. @@ -4802,6 +4867,11 @@ Gets or sets the . + + + Gets or sets the . + + Gets or sets the . @@ -6093,6 +6163,11 @@ Gets or sets a TimeZoneInfo for the serialization and deserialization. + + + Gets or sets a omit values kind for the property value serialization. + + Gets the routing conventions. @@ -14382,19 +14457,3 @@ -ummary> - The value segment. - - - - Gets the value segment. - - - - - - - - - - diff --git a/src/Microsoft.AspNetCore.OData/ODataOptions.cs b/src/Microsoft.AspNetCore.OData/ODataOptions.cs index 500265fa5..794603544 100644 --- a/src/Microsoft.AspNetCore.OData/ODataOptions.cs +++ b/src/Microsoft.AspNetCore.OData/ODataOptions.cs @@ -11,6 +11,7 @@ using System.Diagnostics.Contracts; using Microsoft.AspNetCore.OData.Abstracts; using Microsoft.AspNetCore.OData.Batch; +using Microsoft.AspNetCore.OData.Formatter; using Microsoft.AspNetCore.OData.Routing; using Microsoft.AspNetCore.OData.Routing.Conventions; using Microsoft.Extensions.DependencyInjection; @@ -52,6 +53,11 @@ public class ODataOptions /// public TimeZoneInfo TimeZone { get; set; } = TimeZoneInfo.Local; + /// + /// Gets or sets a omit values kind for the property value serialization. + /// + public OmitValuesKind OmitValuesKind { get; set; } = OmitValuesKind.Unknown; + /// /// Gets the routing conventions. /// diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 3ee9900ec..b23d011c8 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -373,6 +373,10 @@ Microsoft.AspNetCore.OData.Formatter.ODataParameterValue.Value.get -> object Microsoft.AspNetCore.OData.Formatter.ODataUntypedActionParameters Microsoft.AspNetCore.OData.Formatter.ODataUntypedActionParameters.Action.get -> Microsoft.OData.Edm.IEdmAction Microsoft.AspNetCore.OData.Formatter.ODataUntypedActionParameters.ODataUntypedActionParameters(Microsoft.OData.Edm.IEdmAction action) -> void +Microsoft.AspNetCore.OData.Formatter.OmitValuesKind +Microsoft.AspNetCore.OData.Formatter.OmitValuesKind.Defaults = 2 -> Microsoft.AspNetCore.OData.Formatter.OmitValuesKind +Microsoft.AspNetCore.OData.Formatter.OmitValuesKind.Nulls = 1 -> Microsoft.AspNetCore.OData.Formatter.OmitValuesKind +Microsoft.AspNetCore.OData.Formatter.OmitValuesKind.Unknown = 0 -> Microsoft.AspNetCore.OData.Formatter.OmitValuesKind Microsoft.AspNetCore.OData.Formatter.ResourceContext Microsoft.AspNetCore.OData.Formatter.ResourceContext.DynamicComplexProperties.get -> System.Collections.Generic.IDictionary Microsoft.AspNetCore.OData.Formatter.ResourceContext.DynamicComplexProperties.set -> void @@ -460,6 +464,8 @@ Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.Naviga Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.NavigationSource.set -> void Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.ODataSerializerContext() -> void Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.ODataSerializerContext(Microsoft.AspNetCore.OData.Formatter.ResourceContext resource, Microsoft.OData.UriParser.SelectExpandClause selectExpandClause, Microsoft.OData.Edm.IEdmProperty edmProperty) -> void +Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.OmitValuesKind.get -> Microsoft.AspNetCore.OData.Formatter.OmitValuesKind +Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.OmitValuesKind.set -> void Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.Path.get -> Microsoft.OData.UriParser.ODataPath Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.Path.set -> void Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext.QueryOptions.get -> Microsoft.AspNetCore.OData.Query.ODataQueryOptions @@ -666,6 +672,8 @@ Microsoft.AspNetCore.OData.ODataOptions.Expand() -> Microsoft.AspNetCore.OData.O Microsoft.AspNetCore.OData.ODataOptions.Filter() -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.GetRouteServices(string routePrefix) -> System.IServiceProvider Microsoft.AspNetCore.OData.ODataOptions.ODataOptions() -> void +Microsoft.AspNetCore.OData.ODataOptions.OmitValuesKind.get -> Microsoft.AspNetCore.OData.Formatter.OmitValuesKind +Microsoft.AspNetCore.OData.ODataOptions.OmitValuesKind.set -> void Microsoft.AspNetCore.OData.ODataOptions.OrderBy() -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.QuerySettings.get -> Microsoft.OData.ModelBuilder.Config.DefaultQuerySettings Microsoft.AspNetCore.OData.ODataOptions.RouteComponents.get -> System.Collections.Generic.IDictionary @@ -1610,6 +1618,7 @@ static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetETagHandle static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetModel(this Microsoft.AspNetCore.Http.HttpRequest request) -> Microsoft.OData.Edm.IEdmModel static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetNextPageLink(this Microsoft.AspNetCore.Http.HttpRequest request, int pageSize, object instance, System.Func objectToSkipTokenValue) -> System.Uri static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetODataVersion(this Microsoft.AspNetCore.Http.HttpRequest request) -> Microsoft.OData.ODataVersion +static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetOmitValuesKind(this Microsoft.AspNetCore.Http.HttpRequest request) -> Microsoft.AspNetCore.OData.Formatter.OmitValuesKind static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetReaderSettings(this Microsoft.AspNetCore.Http.HttpRequest request) -> Microsoft.OData.ODataMessageReaderSettings static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetRouteServices(this Microsoft.AspNetCore.Http.HttpRequest request) -> System.IServiceProvider static Microsoft.AspNetCore.OData.Extensions.HttpRequestExtensions.GetTimeZoneInfo(this Microsoft.AspNetCore.Http.HttpRequest request) -> System.TimeZoneInfo @@ -1736,7 +1745,9 @@ virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataEdmTypeSerialize virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataEnumSerializer.CreateODataEnumValue(object graph, Microsoft.OData.Edm.IEdmEnumTypeReference enumType, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> Microsoft.OData.ODataEnumValue virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataPrimitiveSerializer.CreateODataPrimitiveValue(object graph, Microsoft.OData.Edm.IEdmPrimitiveTypeReference primitiveType, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> Microsoft.OData.ODataPrimitiveValue virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.AppendDynamicProperties(Microsoft.OData.ODataResource resource, Microsoft.AspNetCore.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> void +virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateComplexNestedResourceInfo(Microsoft.OData.Edm.IEdmStructuralProperty complexProperty, Microsoft.OData.UriParser.PathSelectItem pathSelectItem, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataNestedResourceInfo virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateComputedProperty(string propertyName, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataProperty +virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateDynamicComplexNestedResourceInfo(string propertyName, object propertyValue, Microsoft.OData.Edm.IEdmTypeReference edmType, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataNestedResourceInfo virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateETag(Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> string virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateNavigationLink(Microsoft.OData.Edm.IEdmNavigationProperty navigationProperty, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataNestedResourceInfo virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateODataAction(Microsoft.OData.Edm.IEdmAction action, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext) -> Microsoft.OData.ODataAction From 57bf43bc7a5f9755f28e37e3564a4ba0a347f6c3 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Wed, 10 Aug 2022 11:30:48 -0700 Subject: [PATCH 2/2] Update src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs Co-authored-by: Christof Sprenger <53094940+chrisspre@users.noreply.github.com> --- .../Extensions/HttpRequestExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs index 2b6c2a23c..5ed748969 100644 --- a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs @@ -88,7 +88,7 @@ public static OmitValuesKind GetOmitValuesKind(this HttpRequest request) string preferHeader = RequestPreferenceHelpers.GetRequestPreferHeader(request.Headers); if (preferHeader != null) { - // simply use the string comparision case-insensitive + // use case insensitive string comparison if (preferHeader.Contains("omit-values=nulls", StringComparison.OrdinalIgnoreCase)) { return OmitValuesKind.Nulls;