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

Add support for EnableConstantParameterization #215

Merged
merged 5 commits into from
Jun 12, 2024
Merged
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
24 changes: 20 additions & 4 deletions AutoMapper.AspNetCore.OData.EF6/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ public static class QueryableExtensions
public static ICollection<TModel> Get<TModel, TData>(this IQueryable<TData> query, IMapper mapper, ODataQueryOptions<TModel> options, QuerySettings querySettings)
where TModel : class
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default);
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>
(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default,
enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true
);
query.ApplyOptions(mapper, filter, options, querySettings);
return query.Get
(
Expand All @@ -50,7 +54,11 @@ public static ICollection<TModel> Get<TModel, TData>(this IQueryable<TData> quer
public static async Task<ICollection<TModel>> GetAsync<TModel, TData>(this IQueryable<TData> query, IMapper mapper, ODataQueryOptions<TModel> options, QuerySettings querySettings = null)
where TModel : class
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default);
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>
(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.Default,
enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true
);
await query.ApplyOptionsAsync(mapper, filter, options, querySettings);
return await query.GetAsync
(
Expand All @@ -75,7 +83,11 @@ public static async Task<ICollection<TModel>> GetAsync<TModel, TData>(this IQuer
public static async Task<IQueryable<TModel>> GetQueryAsync<TModel, TData>(this IQueryable<TData> query, IMapper mapper, ODataQueryOptions<TModel> options, QuerySettings querySettings = null)
where TModel : class
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False);
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>
(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False,
enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true
);
await query.ApplyOptionsAsync(mapper, filter, options, querySettings);
return query.GetQueryable(mapper, options, querySettings, filter);
}
Expand All @@ -93,7 +105,11 @@ public static async Task<IQueryable<TModel>> GetQueryAsync<TModel, TData>(this I
public static IQueryable<TModel> GetQuery<TModel, TData>(this IQueryable<TData> query, IMapper mapper, ODataQueryOptions<TModel> options, QuerySettings querySettings = null)
where TModel : class
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False);
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>
(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False,
enableConstantParameterization: querySettings?.ODataSettings?.EnableConstantParameterization ?? true
);
query.ApplyOptions(mapper, filter, options, querySettings);
return query.GetQueryable(mapper, options, querySettings, filter);
}
Expand Down
27 changes: 20 additions & 7 deletions AutoMapper.AspNetCore.OData.EFCore/LinqExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,20 @@ public static Expression ReplaceParameter(this Expression expression,
/// <typeparam name="T"></typeparam>
/// <param name="filterOption"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> ToFilterExpression<T>(this FilterQueryOption filterOption, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null)
public static Expression<Func<T, bool>> ToFilterExpression<T>(
this FilterQueryOption filterOption,
HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default,
TimeZoneInfo timeZone = null,
bool enableConstantParameterization = true)
{
if (filterOption == null)
return null;

IQueryable queryable = Enumerable.Empty<T>().AsQueryable();

queryable = filterOption.ApplyTo(queryable, new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone });
queryable = filterOption.ApplyTo(
queryable,
new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization });

MethodCallExpression whereMethodCallExpression = (MethodCallExpression)queryable.Expression;

Expand All @@ -53,13 +59,19 @@ public static Expression<Func<T, bool>> ToFilterExpression<T>(this FilterQueryOp
/// <typeparam name="T"></typeparam>
/// <param name="filterOption"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> ToSearchExpression<T>(this SearchQueryOption filterOption, HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default, TimeZoneInfo timeZone = null)
public static Expression<Func<T, bool>> ToSearchExpression<T>(
this SearchQueryOption filterOption,
HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default,
TimeZoneInfo timeZone = null,
bool enableConstantParameterization = true)
{
if (filterOption == null)
return null;

IQueryable queryable = Enumerable.Empty<T>().AsQueryable();
queryable = filterOption.ApplyTo(queryable, new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone });
queryable = filterOption.ApplyTo(
queryable,
new ODataQuerySettings() { HandleNullPropagation = handleNullPropagation, TimeZone = timeZone, EnableConstantParameterization = enableConstantParameterization });

MethodCallExpression whereMethodCallExpression = (MethodCallExpression)queryable.Expression;

Expand All @@ -68,7 +80,8 @@ public static Expression<Func<T, bool>> ToSearchExpression<T>(this SearchQueryOp

public static Expression<Func<T, bool>> ToFilterExpression<T>(this ODataQueryOptions<T> options,
HandleNullPropagationOption handleNullPropagation = HandleNullPropagationOption.Default,
TimeZoneInfo timeZone = null)
TimeZoneInfo timeZone = null,
bool enableConstantParameterization = true)
{
if (options is null || options.Filter is null && options.Search is null)
{
Expand All @@ -80,14 +93,14 @@ public static Expression<Func<T, bool>> ToFilterExpression<T>(this ODataQueryOpt
Expression filterExpression = null;
if (options.Filter is not null)
{
var raw = options.Filter.ToFilterExpression<T>(handleNullPropagation, timeZone);
var raw = options.Filter.ToFilterExpression<T>(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<T>(handleNullPropagation, timeZone);
var raw = options.Search.ToSearchExpression<T>(handleNullPropagation, timeZone, enableConstantParameterization);
searchExpression = raw.Body.ReplaceParameter(raw.Parameters[0], parameter);
}

Expand Down
8 changes: 8 additions & 0 deletions AutoMapper.AspNetCore.OData.EFCore/ODataSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,13 @@ public class ODataSettings
/// Default is null.
/// </value>
public TimeZoneInfo TimeZone { get; set; }

/// <summary>
/// Gets or sets a value indicating whether constants should be parameterized.
/// </summary>
/// <value>
/// Default is true.
/// </value>
public bool EnableConstantParameterization { get; set; } = true;
}
}
14 changes: 9 additions & 5 deletions AutoMapper.AspNetCore.OData.EFCore/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public static ICollection<TModel> Get<TModel, TData>(this IQueryable<TData> quer
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False,
querySettings?.ODataSettings?.TimeZone);
querySettings?.ODataSettings?.TimeZone,
querySettings?.ODataSettings?.EnableConstantParameterization ?? true);

query.ApplyOptions(mapper, filter, options, querySettings);
return query.Get
Expand All @@ -36,7 +37,8 @@ public static async Task<ICollection<TModel>> GetAsync<TModel, TData>(this IQuer
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False,
querySettings?.ODataSettings?.TimeZone);
querySettings?.ODataSettings?.TimeZone,
querySettings?.ODataSettings?.EnableConstantParameterization ?? true);
await query.ApplyOptionsAsync(mapper, filter, options, querySettings);
return await query.GetAsync
(
Expand All @@ -52,8 +54,9 @@ public static async Task<IQueryable<TModel>> GetQueryAsync<TModel, TData>(this I
where TModel : class
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False,
querySettings?.ODataSettings?.TimeZone);
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False,
querySettings?.ODataSettings?.TimeZone,
querySettings?.ODataSettings?.EnableConstantParameterization ?? true);

await query.ApplyOptionsAsync(mapper, filter, options, querySettings);
return query.GetQueryable(mapper, options, querySettings, filter);
Expand All @@ -64,7 +67,8 @@ public static IQueryable<TModel> GetQuery<TModel, TData>(this IQueryable<TData>
{
Expression<Func<TModel, bool>> filter = options.ToFilterExpression<TModel>(
querySettings?.ODataSettings?.HandleNullPropagation ?? HandleNullPropagationOption.False,
querySettings?.ODataSettings?.TimeZone);
querySettings?.ODataSettings?.TimeZone,
querySettings?.ODataSettings?.EnableConstantParameterization ?? true);
query.ApplyOptions(mapper, filter, options, querySettings);
return query.GetQueryable(mapper, options, querySettings, filter);
}
Expand Down
54 changes: 48 additions & 6 deletions AutoMapper.OData.EFCore.Tests/GetQueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,36 @@ void Test(ICollection<CoreBuilding> collection)
}
}

[Fact]
public void BuildingsFilterNameDisableConstantParameterization()
{
string query = "/corebuilding?$filter=contains(Name, 'Two L2')";
Test(GetQuery<CoreBuilding, TBuilding>(query, querySettings: new() { ODataSettings = new() { EnableConstantParameterization = false } }));

void Test(IQueryable<CoreBuilding> queryable)
{
string sqlQuery = queryable.ToQueryString();
Assert.Contains("LIKE N'%Two L2%'", sqlQuery);
Assert.DoesNotContain("DECLARE", sqlQuery);
Assert.DoesNotContain("ESCAPE", sqlQuery);
}
}

[Fact]
public void BuildingsFilterNameEnableConstantParameterization()
{
string query = "/corebuilding?$filter=contains(Name, 'Two L2')";
Test(GetQuery<CoreBuilding, TBuilding>(query, querySettings: new() { ODataSettings = new() { EnableConstantParameterization = true } }));

void Test(IQueryable<CoreBuilding> queryable)
{
string sqlQuery = queryable.ToQueryString();
Assert.DoesNotContain("LIKE N'%Two L2%'", sqlQuery);
Assert.Contains("DECLARE", sqlQuery);
Assert.Contains("ESCAPE", sqlQuery);
}
}

[Fact]
public async void OpsTenantOrderByCountOfReference()
{
Expand Down Expand Up @@ -1332,15 +1362,27 @@ IQueryable<TModel> DoGet(IMapper mapper)
}
}

private ICollection<TModel> Get<TModel, TData>(string query, ODataQueryOptions<TModel> options = null, QuerySettings querySettings = null) where TModel : class where TData : class
private IQueryable<TModel> GetQuery<TModel, TData>(string query, ODataQueryOptions<TModel> options = null, QuerySettings querySettings = null) where TModel : class where TData : class
{
return Get
return DoGet
(
query,
serviceProvider.GetRequiredService<MyDbContext>().Set<TData>(),
options,
querySettings
serviceProvider.GetRequiredService<IMapper>()
);

IQueryable<TModel> DoGet(IMapper mapper)
{
return serviceProvider.GetRequiredService<MyDbContext>().Set<TData>().GetQuery
(
mapper,
options ?? GetODataQueryOptions<TModel>(query),
querySettings
);
};
}

private ICollection<TModel> Get<TModel, TData>(string query, ODataQueryOptions<TModel> options = null, QuerySettings querySettings = null) where TModel : class where TData : class
{
return GetQuery<TModel, TData>(query, options, querySettings).ToList();
}

private async Task<ICollection<TModel>> GetAsync<TModel, TData>(string query,
Expand Down
Loading