Skip to content

Commit b5a3d86

Browse files
committed
Merge branch 'dev'
2 parents 06db3e2 + 65e6575 commit b5a3d86

8 files changed

Lines changed: 49 additions & 34 deletions

File tree

.github/workflows/build.yml

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

33
name: Build OfX and Publish
44
env:
5-
OFX_VERSION: 7.2.0-preview
5+
OFX_VERSION: 7.2.0
66

77
on:
88
push:

src/OfX/Helpers/ReflectionHelpers.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,20 @@ IEnumerable<MappableDataProperty> IterateMappableProperties(IEnumerable<Property
7171
continue;
7272
}
7373

74-
var ofXAttribute = property.GetCustomAttributes(true).OfType<OfXAttribute>().FirstOrDefault();
75-
if (ofXAttribute is not null)
74+
var attribute = property.GetCustomAttribute<OfXAttribute>();
75+
if (attribute is not null)
7676
{
7777
var paramExpression = Expression.Parameter(typeof(object), nameof(obj));
7878
var castExpression = Expression.Convert(paramExpression, obj.GetType());
79-
var propExpression = Expression.Property(castExpression, ofXAttribute.PropertyName);
79+
var propExpression = Expression.Property(castExpression, attribute.PropertyName);
8080
var convertToObject = Expression.Convert(propExpression, typeof(object));
8181
var expression = Expression.Lambda<Func<object, object>>(convertToObject, paramExpression);
8282
var func = expression.Compile();
8383
var graph = Graphs.GetOrAdd(obj.GetType(), DependencyGraphBuilder.BuildDependencyGraph);
8484
var order = graph.GetPropertyOrder(property);
85-
yield return new MappableDataProperty(property, obj, ofXAttribute, func, ofXAttribute.Expression,
86-
order);
85+
yield return new MappableDataProperty(property, obj, attribute, func, attribute.Expression, order);
8786
OfXPropertiesCache.TryAdd(property,
88-
new MappableDataPropertyCache(ofXAttribute, func, ofXAttribute.Expression, order));
87+
new MappableDataPropertyCache(attribute, func, attribute.Expression, order));
8988
continue;
9089
}
9190

@@ -163,7 +162,6 @@ internal static void MapResponseData(IEnumerable<MappableDataProperty> mappableP
163162
catch (Exception)
164163
{
165164
if (OfXStatics.ThrowIfExceptions) throw;
166-
// Ignore this field as well
167165
}
168166

169167
return value;

src/OfX/Helpers/RegexHelpers.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ public static partial class RegexHelpers
88
public static string ResolvePlaceholders(string expression, IDictionary<string, string> parameters)
99
{
1010
if (expression is null) return null;
11+
parameters ??= new Dictionary<string, string>();
12+
var lookupCache = new Dictionary<string, string>(parameters.Count, StringComparer.OrdinalIgnoreCase);
13+
foreach (var kvp in parameters) lookupCache[kvp.Key] = kvp.Value;
1114
return ParametersRegex.Replace(expression, match =>
1215
{
1316
var hasParameter = match.Groups["parameter"].Success;
@@ -16,10 +19,7 @@ public static string ResolvePlaceholders(string expression, IDictionary<string,
1619
var hasDefault = match.Groups["default"].Success;
1720
if (!hasDefault) throw new OfXException.InvalidParameter(expression);
1821
var fallback = match.Groups["default"].Value;
19-
20-
if (parameters != null && parameters.TryGetValue(parameter, out var value) && value != null) return value;
21-
22-
return fallback;
22+
return lookupCache.GetValueOrDefault(parameter, fallback);
2323
});
2424
}
2525

src/OfX/Implementations/DataMappableService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public async Task MapDataAsync(object value, object parameters = null, Cancellat
3232

3333
var allPropertyDatas = ReflectionHelpers
3434
.GetMappableProperties(value)
35-
.ToList();
35+
.ToArray();
3636

3737
var ofXTypesData = ReflectionHelpers
3838
.GetOfXTypesData(allPropertyDatas, OfXStatics.OfXAttributeTypes.Value);

src/OfX/Implementations/SendPipelinesOrchestrator.cs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ internal abstract class SendPipelinesOrchestrator
1414
internal abstract Task<ItemsResponse<OfXDataResponse>> ExecuteAsync(OfXRequest message, IContext context);
1515
}
1616

17-
internal sealed class SendPipelinesOrchestrator<TAttribute>(IServiceProvider serviceProvider) :
17+
internal sealed class SendPipelinesOrchestrator<TAttribute>(IServiceProvider serviceProvider) :
1818
SendPipelinesOrchestrator where TAttribute : OfXAttribute
1919
{
2020
internal override async Task<ItemsResponse<OfXDataResponse>> ExecuteAsync(OfXRequest message, IContext context)
@@ -24,18 +24,23 @@ internal override async Task<ItemsResponse<OfXDataResponse>> ExecuteAsync(OfXReq
2424
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
2525
cts.CancelAfter(OfXConstants.DefaultRequestTimeout);
2626
var expressions = JsonSerializer.Deserialize<string[]>(message.Expression);
27-
var expressionsResolved = expressions.Select(originalExpression => new
28-
{
29-
originalExpression, resolvedExpression = context switch
27+
var parameters = context is IExpressionParameters expressionParameters ? expressionParameters.Parameters : null;
28+
29+
// Resolve expressions and build lookup in one pass
30+
31+
var (expressionMap, resolvedExpressions) = expressions.Aggregate((
32+
ExpressionMap: new Dictionary<ExpressionWrapper, string>(expressions.Length),
33+
ResolvedExpressions: new List<string>(expressions.Length)),
34+
(acc, originalExpression) =>
3035
{
31-
IExpressionParameters expressionParameters => RegexHelpers
32-
.ResolvePlaceholders(originalExpression, expressionParameters.Parameters),
33-
_ => RegexHelpers.ResolvePlaceholders(originalExpression, null)
34-
}
35-
}).ToArray();
36+
var resolvedExpression = RegexHelpers.ResolvePlaceholders(originalExpression, parameters);
37+
if (acc.ExpressionMap.TryAdd(new ExpressionWrapper(resolvedExpression), originalExpression))
38+
acc.ResolvedExpressions.Add(resolvedExpression);
39+
return acc;
40+
});
3641

37-
var expression = JsonSerializer.Serialize(expressionsResolved
38-
.Select(a => a.resolvedExpression).Distinct());
42+
// Serialize only the distinct resolved expressions
43+
var expression = JsonSerializer.Serialize(resolvedExpressions);
3944

4045
var request = new RequestOf<TAttribute>(message.SelectorIds, expression);
4146
var requestContext = new RequestContextImpl<TAttribute>(request, context?.Headers ?? [], cts.Token);
@@ -45,16 +50,28 @@ internal override async Task<ItemsResponse<OfXDataResponse>> ExecuteAsync(OfXReq
4550
.Aggregate(() => handler.RequestAsync(requestContext),
4651
(acc, pipeline) => () => pipeline.HandleAsync(requestContext, acc)).Invoke();
4752

48-
result.Items.ForEach(it => it.OfXValues =
49-
[
50-
..expressionsResolved.Select(ex =>
53+
result.Items.ForEach(it =>
54+
{
55+
var valueLookup = it.OfXValues
56+
.ToDictionary(v => new ExpressionWrapper(v.Expression), v => v);
57+
58+
var values = expressionMap.Select(ex =>
5159
{
52-
var valueResult = it.OfXValues.FirstOrDefault(a => a.Expression == ex.resolvedExpression);
60+
var valueResult = valueLookup.GetValueOrDefault(ex.Key, null);
5361
return valueResult is null
5462
? null
55-
: new OfXValueResponse { Expression = ex.originalExpression, Value = valueResult.Value };
56-
}).Where(a => a != null)
57-
]);
63+
: new OfXValueResponse { Expression = ex.Value, Value = valueResult.Value };
64+
}).Where(a => a != null);
65+
it.OfXValues = [..values];
66+
});
5867
return result;
5968
}
69+
}
70+
71+
internal readonly record struct ExpressionWrapper(string Expression)
72+
{
73+
public bool Equals(ExpressionWrapper other) =>
74+
string.Equals(Expression, other.Expression, StringComparison.Ordinal);
75+
76+
public override int GetHashCode() => Expression?.GetHashCode() ?? 0;
6077
}

test/Service1.Contract/Responses/MemberResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class MemberResponse
4141
[ProvinceOf(nameof(ProvinceId), Expression = "CountryId")]
4242
public string CountryId { get; set; }
4343

44-
[CountryOf(nameof(CountryId), Expression = "Provinces[asc Name]")]
44+
[CountryOf(nameof(CountryId), Expression = "Provinces[${Skip|0} ${Take|1} asc Name]")]
4545
public List<ProvinceResponse> Provinces { get; set; }
4646

4747
[CountryOf(nameof(CountryId), Expression = "Provinces[${index|0} ${order|asc} Name]")]

test/Service1.Contract/Responses/SimpleMemberResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ public class SimpleMemberResponse
66
{
77
public string UserId { get; set; }
88

9-
[UserOf(nameof(UserId), Expression = "${UserAlias|Name}")]
9+
[UserOf(nameof(UserId), Expression = "${userAlias|Name}")]
1010
public string UserAlias { get; set; }
1111
}

test/Service1/GraphQls/Query.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public List<SimpleMemberResponse> GetSimpleMembers([Parameters] GetMembersParame
2929
];
3030
}
3131

32-
public sealed record GetMembersParameters(string UserAlias = "Email");
32+
public sealed record GetMembersParameters(string UserAlias = "Email", int Skip = 0, int Take = 1);
3333

3434
public sealed class MembersType : ObjectTypeExtension<MemberResponse>
3535
{

0 commit comments

Comments
 (0)