Skip to content

Commit 1674579

Browse files
AmirEmilVAmir EmilBlaiseD
authored
Feature: Add ApplyFilterAfterProjection option to control OData filter timing (#247)
* feature: add option apply filtering after projection * No need to map the filter for projections. --------- Co-authored-by: Amir Emil <[email protected]> Co-authored-by: Blaise Taylor <[email protected]>
1 parent 30f4952 commit 1674579

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

AutoMapper.AspNetCore.OData.EFCore/ProjectionSettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ namespace AutoMapper.AspNet.OData
1010
public class ProjectionSettings
1111
{
1212
public object Parameters { get; set; }
13+
public bool ApplyFilterAfterProjection { get; set; }
1314
}
1415
}

AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,19 +154,24 @@ private static IQueryable<TModel> GetQuery<TModel, TData>(this IQueryable<TData>
154154
IEnumerable<Expression<Func<TModel, object>>> includeProperties = null,
155155
ProjectionSettings projectionSettings = null)
156156
{
157-
Expression<Func<TData, bool>> f = mapper.MapExpression<Expression<Func<TData, bool>>>(filter);
158157
Func<IQueryable<TData>, IQueryable<TData>> mappedQueryFunc = mapper.MapExpression<Expression<Func<IQueryable<TData>, IQueryable<TData>>>>(queryFunc)?.Compile();
159158

160-
if (filter != null)
161-
query = query.Where(f);
159+
if (filter != null && !FilterAfterProjection())
160+
query = query.Where(mapper.MapExpression<Expression<Func<TData, bool>>>(filter));
162161

163-
return mappedQueryFunc != null
162+
var projectedQuery = mappedQueryFunc != null
164163
? mapper.ProjectTo(mappedQueryFunc(query), projectionSettings?.Parameters, GetIncludes())
165164
: mapper.ProjectTo(query, projectionSettings?.Parameters, GetIncludes());
166165

166+
if (filter != null && FilterAfterProjection())
167+
projectedQuery = projectedQuery.Where(filter);
168+
169+
return projectedQuery;
170+
171+
bool FilterAfterProjection() => projectionSettings?.ApplyFilterAfterProjection ?? false;
167172
Expression<Func<TModel, object>>[] GetIncludes() => includeProperties?.ToArray() ?? new Expression<Func<TModel, object>>[] { };
168173
}
169-
174+
170175
private static void ApplyOptions<TModel, TData>(this IQueryable<TData> query, IMapper mapper, Expression<Func<TModel, bool>> filter, ODataQueryOptions<TModel> options, QuerySettings querySettings)
171176
{
172177
ApplyOptions(options, querySettings);

AutoMapper.OData.EFCore.Tests/GetQueryTests.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,85 @@ void Test(ICollection<OpsTenant> collection)
416416
Assert.Equal("Two", collection.First().Name);
417417
}
418418
}
419+
[Fact]
420+
public async Task FilterByDynamicParameterValueAfterProjectionReturnExpectedResults()
421+
{
422+
// Arrange
423+
string buildingParameterValue = Guid.NewGuid().ToString();
424+
425+
var parameters = new { buildingParameter = buildingParameterValue };
426+
427+
var projectionSettings = new ProjectionSettings
428+
{
429+
Parameters = parameters,
430+
//if false test will fail
431+
ApplyFilterAfterProjection = true
432+
};
433+
434+
var odataSettings = new ODataSettings
435+
{
436+
HandleNullPropagation = HandleNullPropagationOption.False
437+
};
438+
439+
string query = $"/corebuilding?$filter=Parameter eq '{buildingParameterValue}'";
440+
441+
// Act
442+
var result = await GetAsync<CoreBuilding, TBuilding>(
443+
query,
444+
null,
445+
new QuerySettings
446+
{
447+
ProjectionSettings = projectionSettings,
448+
ODataSettings = odataSettings
449+
});
450+
451+
// Assert
452+
if (result.Count == 0)
453+
{
454+
throw new Xunit.Sdk.XunitException(
455+
"Expected at least one CoreBuilding with matching Parameter value, " +
456+
"but none were returned. This replicates the case where a runtime parameter " +
457+
"(like CurrentUserId) was not propagated during OData filter translation."
458+
);
459+
}
419460

461+
Assert.All(result, b => Assert.Equal(buildingParameterValue, b.Parameter));
462+
}
463+
464+
[Fact]
465+
public async Task FilterByDynamicParameterValueBeforeProjectionReturnZeroResults()
466+
{
467+
// Arrange
468+
string buildingParameterValue = Guid.NewGuid().ToString();
469+
470+
var parameters = new { buildingParameter = buildingParameterValue };
471+
472+
var projectionSettings = new ProjectionSettings
473+
{
474+
Parameters = parameters,
475+
ApplyFilterAfterProjection = false
476+
};
477+
478+
var odataSettings = new ODataSettings
479+
{
480+
HandleNullPropagation = HandleNullPropagationOption.False
481+
};
482+
483+
string query = $"/corebuilding?$filter=Parameter eq '{buildingParameterValue}'";
484+
485+
// Act
486+
var result = await GetAsync<CoreBuilding, TBuilding>(
487+
query,
488+
null,
489+
new QuerySettings
490+
{
491+
ProjectionSettings = projectionSettings,
492+
ODataSettings = odataSettings
493+
});
494+
495+
// Assert
496+
Assert.Empty(result);
497+
}
420498
[Fact]
421499
public async Task BuildingExpandBuilderTenantFilterEqAndOrderByWithParameter()
422500
{

0 commit comments

Comments
 (0)