Skip to content

Commit edd6fca

Browse files
committed
cleanup
1 parent ed26071 commit edd6fca

3 files changed

Lines changed: 90 additions & 67 deletions

File tree

NpgsqlRest/NpgsqlRestBuilder.cs

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@ namespace NpgsqlRest;
1111
FrozenDictionary<string, NpgsqlRestMetadataEntry> overloads,
1212
bool hasStreamingEvents);
1313

14+
public class NpgsqlRestMetadataEntry
15+
{
16+
internal NpgsqlRestMetadataEntry(RoutineEndpoint endpoint, IRoutineSourceParameterFormatter formatter, string key)
17+
{
18+
Endpoint = endpoint;
19+
Formatter = formatter;
20+
Key = key;
21+
}
22+
public RoutineEndpoint Endpoint { get; }
23+
public IRoutineSourceParameterFormatter Formatter { get; }
24+
public string Key { get; }
25+
}
26+
1427
public static class NpgsqlRestBuilder
1528
{
1629
public static IApplicationBuilder UseNpgsqlRest(this WebApplication builder, NpgsqlRestOptions options)
@@ -31,8 +44,11 @@ public static IApplicationBuilder UseNpgsqlRest(this WebApplication builder, Npg
3144
var factory = builder.Services.GetRequiredService<ILoggerFactory>();
3245
ILogger? logger = factory.CreateLogger(options.LoggerName ?? typeof(NpgsqlRestBuilder).Namespace ?? "NpgsqlRest");
3346

34-
var (entries,overloads, hasStreamingEvents) =
35-
NpgsqlRestBuilder.Build(logger, builder);
47+
var (
48+
entries,
49+
overloads,
50+
hasStreamingEvents
51+
) = Build(logger, builder);
3652
if (entries.Count == 0)
3753
{
3854
return builder;
@@ -46,20 +62,22 @@ public static IApplicationBuilder UseNpgsqlRest(this WebApplication builder, Npg
4662
foreach (var entry in entries)
4763
{
4864
var handler = new NpgsqlRestEndpoint(entry, overloads, logger);
49-
var routeBuilder = builder.MapMethods(entry.Endpoint.Path, [entry.Endpoint.Method.ToString()], handler.InvokeAsync);
50-
65+
var endpoint = entry.Endpoint;
66+
var methodStr = endpoint.Method.ToString();
67+
var routeBuilder = builder.MapMethods(endpoint.Path, [methodStr], handler.InvokeAsync);
68+
5169
if (options.RouteHandlerCreated is not null)
5270
{
53-
options.RouteHandlerCreated(routeBuilder, entry.Endpoint);
71+
options.RouteHandlerCreated(routeBuilder, endpoint);
5472
}
55-
73+
5674
if (options.LogEndpointCreatedInfo)
5775
{
58-
var urlInfo = string.Concat(entry.Endpoint.Method, " ", entry.Endpoint.Path);
76+
var urlInfo = string.Concat(methodStr, " ", endpoint.Path);
5977
logger?.EndpointCreated(urlInfo);
60-
if (entry.Endpoint.InfoEventsStreamingPath is not null)
78+
if (endpoint.InfoEventsStreamingPath is not null)
6179
{
62-
logger?.EndpointInfoStreamingPath(urlInfo, entry.Endpoint.InfoEventsStreamingPath);
80+
logger?.EndpointInfoStreamingPath(urlInfo, endpoint.InfoEventsStreamingPath);
6381
}
6482
}
6583
}
@@ -71,8 +89,9 @@ public static IApplicationBuilder UseNpgsqlRest(this WebApplication builder, Npg
7189

7290
private static Metadata Build(ILogger? logger, IApplicationBuilder? builder)
7391
{
74-
Dictionary<string, NpgsqlRestMetadataEntry> lookup = [];
75-
Dictionary<string, NpgsqlRestMetadataEntry> overloads = [];
92+
// Pre-size dictionaries with reasonable capacity to reduce allocations
93+
Dictionary<string, NpgsqlRestMetadataEntry> lookup = new(capacity: 128);
94+
Dictionary<string, NpgsqlRestMetadataEntry> overloads = new(capacity: 16);
7695

7796
// Create default upload handlers from upload handler options
7897
Options.UploadOptions.UploadHandlers ??= Options.UploadOptions.CreateUploadHandlers();
@@ -114,28 +133,27 @@ private static Metadata Build(ILogger? logger, IApplicationBuilder? builder)
114133

115134
foreach (var (routine, formatter) in source.Read(builder?.ApplicationServices, defaultStrategy, logger))
116135
{
117-
RoutineEndpoint endpoint = DefaultEndpoint.Create(routine, logger)!;
136+
RoutineEndpoint? endpoint = DefaultEndpoint.Create(routine, logger);
118137

119138
if (endpoint is null)
120139
{
121140
continue;
122141
}
123-
142+
124143
if (Options.EndpointCreated is not null)
125144
{
126145
Options.EndpointCreated(endpoint);
146+
if (endpoint is null)
147+
{
148+
continue;
149+
}
127150
}
128151

129-
if (endpoint is null)
130-
{
131-
continue;
132-
}
133-
134152
if (defaultStrategy is not null && endpoint.RetryStrategy is null)
135153
{
136154
endpoint.RetryStrategy = defaultStrategy;
137155
}
138-
156+
139157
if (endpoint.Path.Length == 0)
140158
{
141159
throw new ArgumentException($"URL path for URL {endpoint.Path}, routine {routine.Name} is empty.");
@@ -146,18 +164,20 @@ private static Metadata Build(ILogger? logger, IApplicationBuilder? builder)
146164
throw new ArgumentException($"URL path for URL {endpoint.Path}, routine {routine.Name} length exceeds {MaxPathLength} characters.");
147165
}
148166

167+
// Cache method string to avoid repeated ToString() calls
149168
var method = endpoint.Method.ToString();
150169
if (endpoint.HasBodyParameter is true && endpoint.RequestParamType == RequestParamType.BodyJson)
151170
{
152171
endpoint.RequestParamType = RequestParamType.QueryString;
153-
logger?.EndpointTypeChangedBodyParam(method, endpoint.Path, endpoint!.BodyParameterName ?? "");
172+
logger?.EndpointTypeChangedBodyParam(method, endpoint.Path, endpoint.BodyParameterName ?? "");
154173
}
155174
if (endpoint.Upload is true)
156175
{
157176
if (endpoint.Method != Method.POST)
158177
{
159178
logger?.EndpointMethodChangedUpload(method, endpoint.Path, Method.POST.ToString());
160179
endpoint.Method = Method.POST;
180+
method = "POST"; // Update cached string
161181
}
162182
if (endpoint.RequestParamType == RequestParamType.BodyJson)
163183
{
@@ -166,8 +186,8 @@ private static Metadata Build(ILogger? logger, IApplicationBuilder? builder)
166186
}
167187
}
168188

169-
var key = string.Concat(method, endpoint?.Path);
170-
var value = new NpgsqlRestMetadataEntry(endpoint!, formatter, key);
189+
var key = string.Concat(method, endpoint.Path);
190+
var value = new NpgsqlRestMetadataEntry(endpoint, formatter, key);
171191
if (lookup.TryGetValue(key, out var existing))
172192
{
173193
overloads[string.Concat(key, existing.Endpoint.Routine.ParamCount)] = existing;
@@ -196,40 +216,36 @@ private static Metadata Build(ILogger? logger, IApplicationBuilder? builder)
196216
{
197217
foreach (var handler in Options.EndpointCreateHandlers)
198218
{
199-
handler.Handle(endpoint!);
219+
handler.Handle(endpoint);
200220
}
201221
}
202222

203-
if (endpoint?.InfoEventsStreamingPath is not null)
223+
if (endpoint.InfoEventsStreamingPath is not null)
204224
{
205225
if (endpoint.InfoEventsStreamingPath.StartsWith(endpoint.Path) is false)
206226
{
207-
endpoint.InfoEventsStreamingPath = string.Concat(
208-
endpoint.Path.EndsWith('/') ? endpoint.Path[..^1] : endpoint.Path , "/",
209-
endpoint.InfoEventsStreamingPath.StartsWith('/') ? endpoint.InfoEventsStreamingPath[1..] : endpoint.InfoEventsStreamingPath);
227+
// Optimize path concatenation
228+
var basePath = endpoint.Path.EndsWith('/') ? endpoint.Path[..^1] : endpoint.Path;
229+
var streamPath = endpoint.InfoEventsStreamingPath.StartsWith('/')
230+
? endpoint.InfoEventsStreamingPath[1..]
231+
: endpoint.InfoEventsStreamingPath;
232+
endpoint.InfoEventsStreamingPath = string.Concat(basePath, "/", streamPath);
210233
}
211234

212235
NpgsqlRestNoticeEventSource.Paths.Add(endpoint.InfoEventsStreamingPath);
213-
214-
if (hasStreamingEvents is false)
215-
{
216-
hasStreamingEvents = true;
217-
}
236+
hasStreamingEvents = true;
218237
}
219-
220-
if (endpoint?.Login is true)
238+
239+
if (endpoint.Login is true)
221240
{
222-
if (hasLogin is false)
223-
{
224-
hasLogin = true;
225-
}
241+
hasLogin = true;
226242
if (routine.IsVoid is true || routine.ReturnsUnnamedSet is true)
227243
{
228244
throw new ArgumentException($"{routine.Type.ToString().ToLowerInvariant()} {routine.Schema}.{routine.Name} is marked as login and it can't be void or returning unnamed data sets.");
229245
}
230246
}
231247

232-
if (endpoint?.Cached is true && hasCachedRoutine is false)
248+
if (endpoint.Cached is true && hasCachedRoutine is false)
233249
{
234250
hasCachedRoutine = true;
235251
}
@@ -264,19 +280,41 @@ private static Metadata Build(ILogger? logger, IApplicationBuilder? builder)
264280
Options.UploadOptions.DefaultUploadHandler);
265281
}
266282

267-
var entries = lookup.Values.ToList();
283+
// Avoid multiple enumerations by creating the list once
284+
var entries = new List<NpgsqlRestMetadataEntry>(lookup.Values);
285+
286+
// Create array once if needed by callbacks or handlers
287+
RoutineEndpoint[]? endpointsArray = null;
288+
bool arrayPopulated = false;
289+
268290
if (Options.EndpointsCreated is not null)
269291
{
270-
Options.EndpointsCreated([.. entries.Select(x => x.Endpoint)]);
292+
endpointsArray = new RoutineEndpoint[entries.Count];
293+
for (int i = 0; i < entries.Count; i++)
294+
{
295+
endpointsArray[i] = entries[i].Endpoint;
296+
}
297+
arrayPopulated = true;
298+
Options.EndpointsCreated(endpointsArray);
271299
}
272300

273301
if (builder is not null)
274302
{
275-
RoutineEndpoint[]? array = null;
276303
foreach (var handler in Options.EndpointCreateHandlers)
277304
{
278-
array ??= [.. entries.Select(x => x.Endpoint)];
279-
handler.Cleanup(array);
305+
if (endpointsArray is null)
306+
{
307+
endpointsArray = new RoutineEndpoint[entries.Count];
308+
}
309+
if (!arrayPopulated)
310+
{
311+
for (int i = 0; i < entries.Count; i++)
312+
{
313+
endpointsArray[i] = entries[i].Endpoint;
314+
}
315+
arrayPopulated = true;
316+
}
317+
handler.Cleanup(endpointsArray);
280318
handler.Cleanup();
281319
}
282320
}

NpgsqlRest/NpgsqlRestMetadataEntry.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

NpgsqlRest/NpgsqlRestNoticeEventSource.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using Microsoft.AspNetCore.Routing;
2-
using Npgsql;
1+
using Npgsql;
32
using NpgsqlRest.Defaults;
3+
using static NpgsqlRest.NpgsqlRestOptions;
44

55
namespace NpgsqlRest;
66

@@ -29,9 +29,9 @@ public async Task InvokeAsync(HttpContext context)
2929
context.Response.Headers.CacheControl = "no-cache, no-store, must-revalidate, max-age=0";
3030
context.Response.Headers.Connection = "keep-alive";
3131

32-
if (NpgsqlRestOptions.Options.CustomServerSentEventsResponseHeaders.Count > 0)
32+
if (Options.CustomServerSentEventsResponseHeaders.Count > 0)
3333
{
34-
foreach (var header in NpgsqlRestOptions.Options.CustomServerSentEventsResponseHeaders)
34+
foreach (var header in Options.CustomServerSentEventsResponseHeaders)
3535
{
3636
if (context.Response.Headers.ContainsKey(header.Key))
3737
{
@@ -101,7 +101,7 @@ public async Task InvokeAsync(HttpContext context)
101101
bool ok = false;
102102
foreach (var claim in context.User?.Claims ?? [])
103103
{
104-
if (string.Equals(claim.Type, NpgsqlRestOptions.Options.AuthenticationOptions.DefaultRoleClaimType, StringComparison.Ordinal))
104+
if (string.Equals(claim.Type, Options.AuthenticationOptions.DefaultRoleClaimType, StringComparison.Ordinal))
105105
{
106106
if (endpoint?.AuthorizeRoles.Contains(claim.Value) is true)
107107
{
@@ -131,9 +131,9 @@ public async Task InvokeAsync(HttpContext context)
131131
foreach (var claim in context.User?.Claims!)
132132
{
133133
if (
134-
string.Equals(claim.Type, NpgsqlRestOptions.Options.AuthenticationOptions.DefaultUserIdClaimType, StringComparison.Ordinal) ||
135-
string.Equals(claim.Type, NpgsqlRestOptions.Options.AuthenticationOptions.DefaultNameClaimType, StringComparison.Ordinal) ||
136-
string.Equals(claim.Type, NpgsqlRestOptions.Options.AuthenticationOptions.DefaultRoleClaimType, StringComparison.Ordinal)
134+
string.Equals(claim.Type, Options.AuthenticationOptions.DefaultUserIdClaimType, StringComparison.Ordinal) ||
135+
string.Equals(claim.Type, Options.AuthenticationOptions.DefaultNameClaimType, StringComparison.Ordinal) ||
136+
string.Equals(claim.Type, Options.AuthenticationOptions.DefaultRoleClaimType, StringComparison.Ordinal)
137137
)
138138
{
139139
if (infoEventsRoles.Contains(claim.Value) is true)

0 commit comments

Comments
 (0)