Skip to content

Commit e8efe45

Browse files
committed
wip
1 parent 307a3eb commit e8efe45

19 files changed

Lines changed: 228 additions & 66 deletions

.config/dotnet-tools.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": 1,
3+
"isRoot": true,
4+
"tools": {
5+
"csharpier": {
6+
"version": "1.2.6",
7+
"commands": [
8+
"csharpier"
9+
],
10+
"rollForward": false
11+
}
12+
}
13+
}

src/GoatQuery.AspNetCore/src/Attributes/EnableQueryAttribute.cs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ public EnableQueryAttribute(int maxTop, int maxPropertyMappingDepth = 5)
2020

2121
public EnableQueryAttribute() { }
2222

23-
public override void OnActionExecuting(ActionExecutingContext context) { }
24-
2523
public override void OnActionExecuted(ActionExecutedContext context)
2624
{
2725
var result = context.Result as ObjectResult;
@@ -40,7 +38,7 @@ public override void OnActionExecuted(ActionExecutedContext context)
4038
if (!int.TryParse(topQuery.ToString(), out int top) && !string.IsNullOrEmpty(topQuery))
4139
{
4240
context.Result = new BadRequestObjectResult(
43-
new { Message = "The query parameter 'Top' could not be parsed to an integer" }
41+
new { message = "The query parameter 'Top' could not be parsed to an integer" }
4442
);
4543
return;
4644
}
@@ -52,7 +50,7 @@ public override void OnActionExecuted(ActionExecutedContext context)
5250
if (!int.TryParse(skipString, out int skip) && !string.IsNullOrEmpty(skipQuery))
5351
{
5452
context.Result = new BadRequestObjectResult(
55-
new { Message = "The query parameter 'Skip' could not be parsed to an integer" }
53+
new { message = "The query parameter 'Skip' could not be parsed to an integer" }
5654
);
5755
return;
5856
}
@@ -64,7 +62,7 @@ public override void OnActionExecuted(ActionExecutedContext context)
6462
if (!bool.TryParse(countString, out bool count) && !string.IsNullOrEmpty(countString))
6563
{
6664
context.Result = new BadRequestObjectResult(
67-
new { Message = "The query parameter 'Count' could not be parsed to a boolean" }
65+
new { message = "The query parameter 'Count' could not be parsed to a boolean" }
6866
);
6967
return;
7068
}
@@ -81,9 +79,9 @@ public override void OnActionExecuted(ActionExecutedContext context)
8179

8280
var query = new Query()
8381
{
84-
Top = top,
85-
Skip = skip,
86-
Count = count,
82+
Top = string.IsNullOrEmpty(topQuery.ToString()) ? null : top,
83+
Skip = string.IsNullOrEmpty(skipString) ? null : skip,
84+
Count = string.IsNullOrEmpty(countString) ? null : count,
8785
OrderBy = orderbyQuery.ToString(),
8886
Search = search,
8987
Filter = filterQuery.ToString(),
@@ -98,7 +96,14 @@ public override void OnActionExecuted(ActionExecutedContext context)
9896
as ISearchBinder<T>;
9997
}
10098

101-
var applyOptions = _options ?? new QueryOptions();
99+
var applyOptions = _options is not null
100+
? new QueryOptions
101+
{
102+
MaxTop = _options.MaxTop,
103+
MaxPropertyMappingDepth = _options.MaxPropertyMappingDepth,
104+
PropertyNamingPolicy = _options.PropertyNamingPolicy,
105+
}
106+
: new QueryOptions();
102107

103108
// Auto-resolve JsonNamingPolicy from DI if not explicitly set
104109
if (applyOptions.PropertyNamingPolicy is null)

src/GoatQuery.AspNetCore/src/GoatQuery.AspNetCore.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
44
<LangVersion>11.0</LangVersion>
@@ -7,7 +7,7 @@
77
<Nullable>enable</Nullable>
88

99
<PackageId>GoatQuery.AspNetCore</PackageId>
10-
<PackageProjectUrl>https://github.com/goatquery/src/GoatQuery.AspNetCore</PackageProjectUrl>
10+
<PackageProjectUrl>https://github.com/goatquery/goatquery-dotnet</PackageProjectUrl>
1111
<RepositoryUrl>https://github.com/goatquery/goatquery-dotnet</RepositoryUrl>
1212
<RepositoryType>git</RepositoryType>
1313
<Description>AspNetCore extensions for GoatQuery</Description>

src/GoatQuery/src/Ast/InfixExpression.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,4 @@ public InfixExpression(Token token, QueryExpression left, string op)
1010
Left = left;
1111
Operator = op;
1212
}
13-
14-
public InfixExpression(Token token)
15-
: base(token) { }
1613
}

src/GoatQuery/src/Ast/Literals.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ public IntegerLiteral(Token token, int value)
3333
}
3434
}
3535

36+
public sealed class LongLiteral : QueryExpression
37+
{
38+
public long Value { get; set; }
39+
40+
public LongLiteral(Token token, long value)
41+
: base(token)
42+
{
43+
Value = value;
44+
}
45+
}
46+
3647
public sealed class DecimalLiteral : QueryExpression
3748
{
3849
public decimal Value { get; set; }

src/GoatQuery/src/Ast/OrderByAst.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ public OrderByStatement(Token token, List<string> segments, OrderByDirection dir
2121
Segments = segments;
2222
}
2323

24-
public bool IsNestedPath => Segments.Count > 1;
25-
2624
public override string TokenLiteral()
2725
{
2826
return string.Join("/", Segments);

src/GoatQuery/src/Evaluator/FilterEvaluationContext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ int maxPropertyMappingDepth
2323
public bool IsInLambdaScope => LambdaScopes.Count > 0;
2424
public LambdaScope CurrentLambda => LambdaScopes.Peek();
2525

26+
public Expression GetBaseExpression()
27+
{
28+
return IsInLambdaScope ? CurrentLambda.Parameter : RootParameter;
29+
}
30+
2631
public void EnterLambdaScope(
2732
string parameterName,
2833
ParameterExpression parameter,

src/GoatQuery/src/Evaluator/FilterEvaluator.cs

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ private static Result<Expression> EvaluatePropertyPathExpression(
7373
FilterEvaluationContext context
7474
)
7575
{
76-
var baseExpression = context.IsInLambdaScope
77-
? (Expression)context.CurrentLambda.Parameter
78-
: context.RootParameter;
76+
var baseExpression = context.GetBaseExpression();
7977

8078
var propertyPathResult = BuildPropertyPath(
8179
propertyPath,
@@ -89,15 +87,10 @@ FilterEvaluationContext context
8987

9088
if (exp.Right is NullLiteral)
9189
{
92-
var nullComparison = CreateNullComparison(exp, finalProperty);
93-
return nullComparison;
90+
return CreateNullComparison(exp, finalProperty);
9491
}
9592

96-
var comparisonResult = EvaluateValueComparison(exp, finalProperty);
97-
if (comparisonResult.IsFailed)
98-
return comparisonResult;
99-
100-
return comparisonResult.Value;
93+
return EvaluateValueComparison(exp, finalProperty);
10194
}
10295

10396
private static Result<MemberExpression> BuildPropertyPath(
@@ -132,16 +125,6 @@ PropertyMappingTree propertyMappingTree
132125
return Result.Ok((MemberExpression)result.Value);
133126
}
134127

135-
private static bool IsPrimitiveType(Type type)
136-
{
137-
return type.IsPrimitive
138-
|| type == typeof(string)
139-
|| type == typeof(decimal)
140-
|| type == typeof(DateTime)
141-
|| type == typeof(Guid)
142-
|| Nullable.GetUnderlyingType(type) != null;
143-
}
144-
145128
private static Expression CreateNullComparison(InfixExpression exp, MemberExpression property)
146129
{
147130
return exp.Operator == Keywords.Eq
@@ -261,6 +244,7 @@ Expression expression
261244
return literal switch
262245
{
263246
IntegerLiteral intLit => CreateIntegerOrEnumConstant(intLit.Value, expression.Type),
247+
LongLiteral longLit => CreateLongConstant(longLit.Value, expression.Type),
264248
DateLiteral dateLit => Result.Ok(CreateDateConstant(dateLit, expression.Type)),
265249
GuidLiteral guidLit => Result.Ok(Expression.Constant(guidLit.Value, expression.Type)),
266250
DecimalLiteral decLit => Result.Ok(Expression.Constant(decLit.Value, expression.Type)),
@@ -416,9 +400,7 @@ FilterEvaluationContext context
416400
return Result.Fail($"Invalid property '{identifier}' within filter");
417401
}
418402

419-
var baseExpression = context.IsInLambdaScope
420-
? (Expression)context.CurrentLambda.Parameter
421-
: context.RootParameter;
403+
var baseExpression = context.GetBaseExpression();
422404

423405
var identifierProperty = Expression.Property(
424406
baseExpression,
@@ -488,9 +470,7 @@ FilterEvaluationContext context
488470
ParameterExpression Parameter
489471
)> SetupLambdaEvaluation(QueryLambdaExpression lambdaExp, FilterEvaluationContext context)
490472
{
491-
var baseExpression = context.IsInLambdaScope
492-
? (Expression)context.CurrentLambda.Parameter
493-
: context.RootParameter;
473+
var baseExpression = context.GetBaseExpression();
494474

495475
var collectionResult = ResolveCollectionProperty(
496476
lambdaExp.Property,
@@ -629,7 +609,7 @@ FilterEvaluationContext context
629609
)
630610
{
631611
// For primitive types (string, int, etc.), allow direct comparisons with the lambda parameter
632-
if (IsPrimitiveType(context.CurrentLambda.ElementType))
612+
if (PropertyMappingTreeBuilder.IsPrimitiveType(context.CurrentLambda.ElementType))
633613
{
634614
return EvaluateValueComparison(exp, context.CurrentLambda.Parameter);
635615
}
@@ -811,6 +791,39 @@ Type targetType
811791
}
812792
}
813793

794+
private static Result<ConstantExpression> CreateLongConstant(long value, Type targetType)
795+
{
796+
try
797+
{
798+
var type = GetNonNullableType(targetType);
799+
800+
object convertedValue = type switch
801+
{
802+
Type t when t == typeof(long) => value,
803+
Type t when t == typeof(int) => Convert.ToInt32(value),
804+
Type t when t == typeof(short) => Convert.ToInt16(value),
805+
Type t when t == typeof(byte) => Convert.ToByte(value),
806+
Type t when t == typeof(uint) => Convert.ToUInt32(value),
807+
Type t when t == typeof(ulong) => Convert.ToUInt64(value),
808+
Type t when t == typeof(ushort) => Convert.ToUInt16(value),
809+
Type t when t == typeof(sbyte) => Convert.ToSByte(value),
810+
_ => throw new NotSupportedException(
811+
$"Unsupported numeric type: {targetType.Name}"
812+
),
813+
};
814+
815+
return Expression.Constant(convertedValue, targetType);
816+
}
817+
catch (OverflowException)
818+
{
819+
return Result.Fail($"Value {value} is too large for type {targetType.Name}");
820+
}
821+
catch (Exception)
822+
{
823+
return Result.Fail($"Error converting {value} to {targetType.Name}");
824+
}
825+
}
826+
814827
private static Result<ConstantExpression> CreateIntegerOrEnumConstant(
815828
int value,
816829
Type targetType

src/GoatQuery/src/Extensions/QueryableExtension.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ public static Result<QueryResult<T>> Apply<T>(
2121

2222
var type = typeof(T);
2323

24-
var maxDepth =
25-
options?.MaxPropertyMappingDepth ?? new QueryOptions().MaxPropertyMappingDepth;
24+
var maxDepth = options?.MaxPropertyMappingDepth ?? 5;
2625
var namingPolicy = options?.PropertyNamingPolicy;
2726
var propertyMappingTree = PropertyMappingTreeBuilder.BuildMappingTree<T>(
2827
maxDepth,
@@ -106,16 +105,16 @@ public static Result<QueryResult<T>> Apply<T>(
106105
// Skip
107106
if (query.Skip > 0)
108107
{
109-
queryable = queryable.Skip(query.Skip ?? 0);
108+
queryable = queryable.Skip(query.Skip.Value);
110109
}
111110

112111
// Top
113112
if (query.Top > 0)
114113
{
115-
queryable = queryable.Take(query.Top ?? 0);
114+
queryable = queryable.Take(query.Top.Value);
116115
}
117116

118-
if (query.Top <= 0 && options?.MaxTop != null)
117+
if ((query.Top == null || query.Top <= 0) && options?.MaxTop > 0)
119118
{
120119
queryable = queryable.Take(options.MaxTop);
121120
}

src/GoatQuery/src/GoatQuery.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<PackageId>GoatQuery</PackageId>
88
<PackageReadmeFile>README.md</PackageReadmeFile>
9-
<PackageProjectUrl>https://github.com/goatquery/src/GoatQuery</PackageProjectUrl>
9+
<PackageProjectUrl>https://github.com/goatquery/goatquery-dotnet</PackageProjectUrl>
1010
<RepositoryUrl>https://github.com/goatquery/goatquery-dotnet</RepositoryUrl>
1111
<RepositoryType>git</RepositoryType>
1212
<Description>.NET Library to support paging, ordering, filtering, searching and selecting in REST APIs.</Description>

0 commit comments

Comments
 (0)