From b06863398041e5e2dd168792d5c11ad47c6fccb3 Mon Sep 17 00:00:00 2001 From: alexmo16 Date: Fri, 11 Apr 2025 14:50:39 -0400 Subject: [PATCH 1/4] - Add possibility of disabling stable ordering. - Add possibility of disabling default order by clause. --- .../QueryableExtensions.cs | 12 +++++--- .../LinqExtensions.cs | 20 ++++++++----- .../ODataSettings.cs | 11 +++++++- .../QueryableExtensions.cs | 12 +++++--- .../GetQueryTests.cs | 28 +++++++++++++++++++ 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs b/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs index adf801e..d59f7a1 100644 --- a/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs @@ -28,7 +28,8 @@ public static ICollection Get(this IQueryable quer Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true ); query.ApplyOptions(mapper, filter, options, querySettings); return query.Get @@ -56,7 +57,8 @@ public static async Task> GetAsync(this IQuer Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true ); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return await query.GetAsync @@ -85,7 +87,8 @@ public static async Task> GetQueryAsync(this I Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true ); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); @@ -107,7 +110,8 @@ public static IQueryable GetQuery(this IQueryable Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true ); query.ApplyOptions(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); diff --git a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs index 84cf8c7..caf3efd 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs @@ -36,7 +36,8 @@ public static Expression> ToFilterExpression( this FilterQueryOption filterOption, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null, - bool enableConstantParameterization = true) + bool enableConstantParameterization = true, + bool ensureStableOrdering = true) { if (filterOption == null) return null; @@ -45,7 +46,7 @@ public static Expression> ToFilterExpression( queryable = filterOption.ApplyTo( queryable, - new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization }); + new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization, EnsureStableOrdering = ensureStableOrdering }); MethodCallExpression whereMethodCallExpression = (MethodCallExpression)queryable.Expression; @@ -62,7 +63,8 @@ public static Expression> ToSearchExpression( this SearchQueryOption filterOption, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null, - bool enableConstantParameterization = true) + bool enableConstantParameterization = true, + bool ensureStableOrdering = true) { if (filterOption == null) return null; @@ -70,7 +72,7 @@ public static Expression> ToSearchExpression( IQueryable queryable = Enumerable.Empty().AsQueryable(); queryable = filterOption.ApplyTo( queryable, - new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization }); + new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization, EnsureStableOrdering = ensureStableOrdering }); MethodCallExpression whereMethodCallExpression = (MethodCallExpression)queryable.Expression; @@ -80,7 +82,8 @@ public static Expression> ToSearchExpression( public static Expression> ToFilterExpression(this ODataQueryOptions options, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null, - bool enableConstantParameterization = true) + bool enableConstantParameterization = true, + bool ensureStableOrdering = true) { if (options is null || options.Filter is null && options.Search is null) { @@ -92,14 +95,14 @@ public static Expression> ToFilterExpression(this ODataQueryOpt Expression filterExpression = null; if (options.Filter is not null) { - var raw = options.Filter.ToFilterExpression(handleNullPropagation, timeZone, enableConstantParameterization); + var raw = options.Filter.ToFilterExpression(handleNullPropagation, timeZone, enableConstantParameterization, ensureStableOrdering); filterExpression = raw.Body.ReplaceParameter(raw.Parameters[0], parameter); } Expression searchExpression = null; if (options.Search is not null) { - var raw = options.Search.ToSearchExpression(handleNullPropagation, timeZone, enableConstantParameterization); + var raw = options.Search.ToSearchExpression(handleNullPropagation, timeZone, enableConstantParameterization, ensureStableOrdering); searchExpression = raw.Body.ReplaceParameter(raw.Parameters[0], parameter); } @@ -189,6 +192,9 @@ public static Expression GetQueryableMethod(this Expression expression, if (orderByClause is null && skip is null && top is null) return null; + if (orderByClause is null && !oDataSettings.EnsureStableOrdering) + return expression.GetSkipCall(skip).GetTakeCall(top); + if (orderByClause is null && (skip is not null || top is not null)) { var orderBySettings = context.FindSortableProperties(type); diff --git a/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs b/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs index b74111c..fbbf14a 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs @@ -41,7 +41,7 @@ public class ODataSettings /// Default is true. /// public bool EnableConstantParameterization { get; set; } = true; - + /// /// If sets to true, orderBy pk desc will always be present on main entity. /// @@ -58,5 +58,14 @@ public class ODataSettings /// Default is false. /// public bool AlwaysSortByPrimaryKey { get; set; } + + /// + /// Gets or sets a value indicating whether query composition should + /// alter the original query when necessary to ensure a stable sort order. + /// + /// + /// Default is true. + /// + public bool EnsureStableOrdering { get; set; } = true; } } diff --git a/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs b/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs index fcd6f4a..9ef0515 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs @@ -19,7 +19,8 @@ public static ICollection Get(this IQueryable quer Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + querySettings?.ODataSettings?.EnsureStableOrdering ?? true); query.ApplyOptions(mapper, filter, options, querySettings); return query.Get @@ -37,7 +38,8 @@ public static async Task> GetAsync(this IQuer Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + querySettings?.ODataSettings?.EnsureStableOrdering ?? true); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return await query.GetAsync ( @@ -55,7 +57,8 @@ public static async Task> GetQueryAsync(this I Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + querySettings?.ODataSettings?.EnsureStableOrdering ?? true); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); @@ -67,7 +70,8 @@ public static IQueryable GetQuery(this IQueryable Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true, + querySettings?.ODataSettings?.EnsureStableOrdering ?? true); query.ApplyOptions(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); } diff --git a/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs b/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs index 39e1a92..dc9ad54 100644 --- a/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs +++ b/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs @@ -896,6 +896,34 @@ void Test(IQueryable queryable) } } + [Fact] + public void BuildingsFilterNameDisableStableOrdering() + { + string query = "/corebuilding?$filter=contains(Name, 'Two L2')&$top=10"; + Test(GetQuery(query, querySettings: new() { ODataSettings = new() { EnsureStableOrdering = false } })); + + void Test(IQueryable queryable) + { + string sqlQuery = queryable.ToQueryString(); + Assert.Contains("TOP", sqlQuery); + Assert.DoesNotContain("ORDER BY [o].[Identifier]", sqlQuery); + } + } + + [Fact] + public void BuildingsFilterNameEnableStableOrdering() + { + string query = "/corebuilding?$filter=contains(Name, 'Two L2')&$top=10"; + Test(GetQuery(query, querySettings: new() { ODataSettings = new() { EnsureStableOrdering = true } })); + + void Test(IQueryable queryable) + { + string sqlQuery = queryable.ToQueryString(); + Assert.Contains("TOP", sqlQuery); + Assert.Contains("ORDER BY [o].[Identifier]", sqlQuery); + } + } + [Fact] public async Task OpsTenantOrderByCountOfReference() { From 8e88fb5b7ba195783d496f6901a19a9836bb180e Mon Sep 17 00:00:00 2001 From: alexmo16 Date: Fri, 11 Apr 2025 15:12:59 -0400 Subject: [PATCH 2/4] fix tests --- AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs index caf3efd..e9c7ad4 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs @@ -192,7 +192,8 @@ public static Expression GetQueryableMethod(this Expression expression, if (orderByClause is null && skip is null && top is null) return null; - if (orderByClause is null && !oDataSettings.EnsureStableOrdering) + bool ensureStableOrdering = oDataSettings?.EnsureStableOrdering ?? true; + if (orderByClause is null && !ensureStableOrdering) return expression.GetSkipCall(skip).GetTakeCall(top); if (orderByClause is null && (skip is not null || top is not null)) From 2b5de890c57195cab4abcf5fbe0f2ba8f52d8657 Mon Sep 17 00:00:00 2001 From: alexmo16 Date: Mon, 14 Apr 2025 11:01:59 -0400 Subject: [PATCH 3/4] instead of adding the EnsureStableOrdering reuse the AlwaysSortByPrimaryKey To fix the problem, I fix the code where ordering by primary key was added even when AlwaysSortByPrimaryKey was disabled. --- .../QueryableExtensions.cs | 12 +++---- .../LinqExtensions.cs | 26 ++++++-------- .../ODataSettings.cs | 9 ----- .../QueryableExtensions.cs | 12 +++---- .../GetQueryTests.cs | 36 ++++++++++++++++--- 5 files changed, 51 insertions(+), 44 deletions(-) diff --git a/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs b/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs index d59f7a1..adf801e 100644 --- a/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs @@ -28,8 +28,7 @@ public static ICollection Get(this IQueryable quer Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true ); query.ApplyOptions(mapper, filter, options, querySettings); return query.Get @@ -57,8 +56,7 @@ public static async Task> GetAsync(this IQuer Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true ); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return await query.GetAsync @@ -87,8 +85,7 @@ public static async Task> GetQueryAsync(this I Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true ); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); @@ -110,8 +107,7 @@ public static IQueryable GetQuery(this IQueryable Expression> filter = options.ToFilterExpression ( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, - enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - ensureStableOrdering: querySettings?.ODataSettings?.EnsureStableOrdering ?? true + enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true ); query.ApplyOptions(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); diff --git a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs index e9c7ad4..2dbacb6 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs @@ -36,8 +36,7 @@ public static Expression> ToFilterExpression( this FilterQueryOption filterOption, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null, - bool enableConstantParameterization = true, - bool ensureStableOrdering = true) + bool enableConstantParameterization = true) { if (filterOption == null) return null; @@ -46,7 +45,7 @@ public static Expression> ToFilterExpression( queryable = filterOption.ApplyTo( queryable, - new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization, EnsureStableOrdering = ensureStableOrdering }); + new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization }); MethodCallExpression whereMethodCallExpression = (MethodCallExpression)queryable.Expression; @@ -63,8 +62,7 @@ public static Expression> ToSearchExpression( this SearchQueryOption filterOption, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null, - bool enableConstantParameterization = true, - bool ensureStableOrdering = true) + bool enableConstantParameterization = true) { if (filterOption == null) return null; @@ -72,7 +70,7 @@ public static Expression> ToSearchExpression( IQueryable queryable = Enumerable.Empty().AsQueryable(); queryable = filterOption.ApplyTo( queryable, - new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization, EnsureStableOrdering = ensureStableOrdering }); + new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization }); MethodCallExpression whereMethodCallExpression = (MethodCallExpression)queryable.Expression; @@ -82,8 +80,7 @@ public static Expression> ToSearchExpression( public static Expression> ToFilterExpression(this ODataQueryOptions options, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null, - bool enableConstantParameterization = true, - bool ensureStableOrdering = true) + bool enableConstantParameterization = true) { if (options is null || options.Filter is null && options.Search is null) { @@ -95,14 +92,14 @@ public static Expression> ToFilterExpression(this ODataQueryOpt Expression filterExpression = null; if (options.Filter is not null) { - var raw = options.Filter.ToFilterExpression(handleNullPropagation, timeZone, enableConstantParameterization, ensureStableOrdering); + var raw = options.Filter.ToFilterExpression(handleNullPropagation, timeZone, enableConstantParameterization); filterExpression = raw.Body.ReplaceParameter(raw.Parameters[0], parameter); } Expression searchExpression = null; if (options.Search is not null) { - var raw = options.Search.ToSearchExpression(handleNullPropagation, timeZone, enableConstantParameterization, ensureStableOrdering); + var raw = options.Search.ToSearchExpression(handleNullPropagation, timeZone, enableConstantParameterization); searchExpression = raw.Body.ReplaceParameter(raw.Parameters[0], parameter); } @@ -190,11 +187,7 @@ public static Expression GetQueryableMethod(this Expression expression, } if (orderByClause is null && skip is null && top is null) - return null; - - bool ensureStableOrdering = oDataSettings?.EnsureStableOrdering ?? true; - if (orderByClause is null && !ensureStableOrdering) - return expression.GetSkipCall(skip).GetTakeCall(top); + return null; if (orderByClause is null && (skip is not null || top is not null)) { @@ -203,6 +196,9 @@ public static Expression GetQueryableMethod(this Expression expression, if (orderBySettings is null) return null; + if (oDataSettings?.AlwaysSortByPrimaryKey is false) + return expression.GetSkipCall(skip).GetTakeCall(top); + return expression .GetDefaultOrderByCall(orderBySettings) .GetSkipCall(skip) diff --git a/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs b/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs index fbbf14a..b1ec176 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs @@ -58,14 +58,5 @@ public class ODataSettings /// Default is false. /// public bool AlwaysSortByPrimaryKey { get; set; } - - /// - /// Gets or sets a value indicating whether query composition should - /// alter the original query when necessary to ensure a stable sort order. - /// - /// - /// Default is true. - /// - public bool EnsureStableOrdering { get; set; } = true; } } diff --git a/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs b/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs index 9ef0515..fcd6f4a 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs @@ -19,8 +19,7 @@ public static ICollection Get(this IQueryable quer Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - querySettings?.ODataSettings?.EnsureStableOrdering ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true); query.ApplyOptions(mapper, filter, options, querySettings); return query.Get @@ -38,8 +37,7 @@ public static async Task> GetAsync(this IQuer Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - querySettings?.ODataSettings?.EnsureStableOrdering ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return await query.GetAsync ( @@ -57,8 +55,7 @@ public static async Task> GetQueryAsync(this I Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - querySettings?.ODataSettings?.EnsureStableOrdering ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true); await query.ApplyOptionsAsync(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); @@ -70,8 +67,7 @@ public static IQueryable GetQuery(this IQueryable Expression> filter = options.ToFilterExpression( querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False, querySettings?.ODataSettings?.TimeZone, - querySettings?.ODataSettings?.EnableConstantParameterization ?? true, - querySettings?.ODataSettings?.EnsureStableOrdering ?? true); + querySettings?.ODataSettings?.EnableConstantParameterization ?? true); query.ApplyOptions(mapper, filter, options, querySettings); return query.GetQueryable(mapper, options, querySettings, filter); } diff --git a/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs b/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs index dc9ad54..ce07465 100644 --- a/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs +++ b/AutoMapper.OData.EFCore.Tests/GetQueryTests.cs @@ -897,10 +897,10 @@ void Test(IQueryable queryable) } [Fact] - public void BuildingsFilterNameDisableStableOrdering() + public void BuildingsFilterNameWithTopAndDisabledAlwaysSortByPrimaryKey() { string query = "/corebuilding?$filter=contains(Name, 'Two L2')&$top=10"; - Test(GetQuery(query, querySettings: new() { ODataSettings = new() { EnsureStableOrdering = false } })); + Test(GetQuery(query, querySettings: new() { ODataSettings = new() { AlwaysSortByPrimaryKey = false } })); void Test(IQueryable queryable) { @@ -911,10 +911,10 @@ void Test(IQueryable queryable) } [Fact] - public void BuildingsFilterNameEnableStableOrdering() + public void BuildingsFilterNameWithTopAndEnabledAlwaysSortByPrimaryKey() { string query = "/corebuilding?$filter=contains(Name, 'Two L2')&$top=10"; - Test(GetQuery(query, querySettings: new() { ODataSettings = new() { EnsureStableOrdering = true } })); + Test(GetQuery(query, querySettings: new() { ODataSettings = new() { AlwaysSortByPrimaryKey = true } })); void Test(IQueryable queryable) { @@ -924,6 +924,34 @@ void Test(IQueryable queryable) } } + [Fact] + public void BuildingsFilterNameWithSkipAndDisabledAlwaysSortByPrimaryKey() + { + string query = "/corebuilding?$filter=contains(Name, 'Two L2')&$skip=10"; + Test(GetQuery(query, querySettings: new() { ODataSettings = new() { AlwaysSortByPrimaryKey = false } })); + + void Test(IQueryable queryable) + { + string sqlQuery = queryable.ToQueryString(); + Assert.Contains("OFFSET", sqlQuery); + Assert.DoesNotContain("ORDER BY [o].[Identifier]", sqlQuery); + } + } + + [Fact] + public void BuildingsFilterNameWithSkipAndEnabledAlwaysSortByPrimaryKey() + { + string query = "/corebuilding?$filter=contains(Name, 'Two L2')&$skip=10"; + Test(GetQuery(query, querySettings: new() { ODataSettings = new() { AlwaysSortByPrimaryKey = true } })); + + void Test(IQueryable queryable) + { + string sqlQuery = queryable.ToQueryString(); + Assert.Contains("OFFSET", sqlQuery); + Assert.Contains("ORDER BY [o].[Identifier]", sqlQuery); + } + } + [Fact] public async Task OpsTenantOrderByCountOfReference() { From a746eec59e0356c71a67ade0e8bb06760d2d172c Mon Sep 17 00:00:00 2001 From: alexmo16 Date: Mon, 14 Apr 2025 11:03:13 -0400 Subject: [PATCH 4/4] formatting --- AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs index 2dbacb6..b8948de 100644 --- a/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs +++ b/AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs @@ -187,7 +187,7 @@ public static Expression GetQueryableMethod(this Expression expression, } if (orderByClause is null && skip is null && top is null) - return null; + return null; if (orderByClause is null && (skip is not null || top is not null)) {