-
Notifications
You must be signed in to change notification settings - Fork 10
feat: .NET 10 #251
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
base: develop
Are you sure you want to change the base?
feat: .NET 10 #251
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Currently waiting on Npgsql.EntityFrameworkCore.PostgreSQL to release the .NET 10 version |
This reverts commit 89dadb9.
* fix: dotnet 10 openapi * fix: use same Title * fix: use better operationId transform * fix: break operationId "DevicePair" into separate endpoints
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR upgrades the project from .NET 9 to .NET 10, scheduled for release after November 11th, 2025. The upgrade includes updating the SDK, runtime, packages, and migrating from Swashbuckle/Swagger to the native Microsoft.AspNetCore.OpenApi implementation.
- Updated .NET SDK and runtime to version 10.0
- Migrated from Swashbuckle to Microsoft.AspNetCore.OpenApi with new document generation and Scalar API viewer integration
- Updated NuGet packages to their .NET 10 compatible versions
- Applied code refactorings leveraging C# 13 features (field keyword) and API improvements
Reviewed changes
Copilot reviewed 43 out of 43 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| global.json | Updated SDK version to 10.0.0 and added Microsoft.Testing.Platform test runner configuration |
| docker/*.Dockerfile | Updated base images from dotnet 9.0-alpine to 10.0-alpine |
| Directory.Packages.props | Updated all Microsoft packages and dependencies to version 10.0.0, removed Swashbuckle package |
| Directory.Build.props | Updated target framework to net10.0 and added OpenAPI interceptors namespace |
| LiveControlGateway/Program.cs, Cron/Program.cs, API/Program.cs | Replaced AddSwaggerExt with AddOpenApiExt and updated namespace imports |
| LiveControlGateway/Controllers/HubControllerBase.cs | Refactored property to use C# 13 field keyword for cleaner auto-property backing field access |
| Common/Utils/TrustedProxiesFetcher.cs | Simplified lambda expression removing unnecessary parameter specification |
| Common/Utils/ConfigureSwaggerOptions.cs | Removed - functionality migrated to new OpenAPI implementation |
| Common/Swagger/* | Removed Swashbuckle-specific files including SwaggerGenExtensions.cs and AttributeFilter.cs |
| Common/OpenAPI/OpenApiExtensions.cs | New OpenAPI document configuration with output caching and custom operation ID generation |
| Common/OpenAPI/DocumentDefaults.cs | New document transformer defining API info, servers, and security schemes |
| Common/OpenShockServiceHelper.cs | Updated API versioning configuration for compatibility with new OpenAPI generation |
| Common/OpenShockMiddlewareHelper.cs | Replaced UseSwagger with MapOpenApi, updated Scalar viewer configuration, changed KnownNetworks to KnownIPNetworks |
| Common/Services/Session/SessionService.cs | Changed ToArrayAsync to ToListAsync and updated Length to Count for consistency |
| Common/Hubs/UserHub.cs | Changed ToArrayAsync to ToListAsync for consistency with EF Core best practices |
| Common/DataAnnotations/*.cs | Removed IParameterAttribute interface implementations and OpenAPI-specific Apply methods |
| Common/DataAnnotations/OpenApiSchemas.cs | Removed - schemas now handled through native OpenAPI mechanisms |
| Common/DataAnnotations/Interfaces/* | Removed custom attribute interfaces as they're replaced by native OpenAPI extensibility |
| Common/Common.csproj | Added Microsoft.AspNetCore.OpenApi package, removed Swashbuckle.AspNetCore.SwaggerGen |
| API/Services/LCGNodeProvisioner/LCGNodeProvisioner.cs | Changed ToArrayAsync to ToListAsync and Length to Count |
| API/Controller/Sessions/ListSessions.cs | Refactored to return IAsyncEnumerable directly with Select instead of manual yield |
| API/Controller/OAuth/_ApiController.cs | Added EndpointGroupName attribute for API grouping, consolidated ApiVersion attribute |
| API/Controller/OAuth/*.cs | Removed ApiExplorerSettings(IgnoreApi = true) to include OAuth endpoints in OpenAPI docs |
| API/Controller/Device/Pair.cs | Split endpoint into two methods (Pair and PairDeprecated) with separate EndpointName attributes and shared PairInternal helper |
| API/Controller/Admin/_ApiController.cs | Added EndpointGroupName attribute for admin API grouping |
| API/Controller/Admin/GetOnlineDevices.cs | Changed ToArrayAsync to ToListAsync |
| API.IntegrationTests/Tests/LcgAssignmentTests.cs | Changed ToArrayAsync to ToListAsync in test cleanup |
| .github/workflows/*.yml | Updated DOTNET_VERSION to 10.x.x and 10.0.x, modified test command syntax |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| using (var tempProvider = builder.Services.BuildServiceProvider()) | ||
| { | ||
| var apiVersionProvider = tempProvider.GetRequiredService<IApiVersionDescriptionProvider>(); | ||
|
|
||
| // Configure OpenAPI for each API version | ||
| foreach (var description in apiVersionProvider.ApiVersionDescriptions) | ||
| { | ||
| builder.Services.AddOpenApi(description.GroupName, options => | ||
| { | ||
| options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1; | ||
| options.AddDocumentTransformer(DocumentDefaults.GetDocumentTransformer( | ||
| version: description.ApiVersion.ToString())); | ||
| options.CreateSchemaReferenceId = (type) => | ||
| { | ||
| var defaultName = type.Type.Name; | ||
| var cleanName = defaultName | ||
| .Replace("[]", "Array") | ||
| .Replace("`1", "Of") | ||
| .Replace("`2", "OfTwo"); | ||
| return cleanName; | ||
| }; | ||
| options.AddOperationTransformer((operation, context, cancellationToken) => | ||
| { | ||
| var actionDescriptor = context.Description.ActionDescriptor; | ||
|
|
||
| // Use endpoint name if available | ||
| var endpointName = actionDescriptor.EndpointMetadata | ||
| .OfType<EndpointNameMetadata>() | ||
| .FirstOrDefault()?.EndpointName; | ||
|
|
||
| if (!string.IsNullOrEmpty(endpointName)) | ||
| { | ||
| operation.OperationId = endpointName; | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| // For controllers | ||
| var controller = actionDescriptor.RouteValues.TryGetValue("controller", out var ctrl) ? ctrl : null; | ||
| var action = actionDescriptor.RouteValues.TryGetValue("action", out var act) ? act : null; | ||
|
|
||
| if (!string.IsNullOrEmpty(controller) && !string.IsNullOrEmpty(action)) | ||
| { | ||
| operation.OperationId = $"{controller}{action}"; | ||
| } | ||
|
|
||
| return Task.CompletedTask; | ||
| }); | ||
| }); | ||
| } | ||
| } | ||
|
|
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Building a temporary service provider within the service configuration can lead to issues. This pattern captures services at configuration time and may not include all registered services or properly dispose of them. Consider using IConfigureOptions pattern or deferring this logic to application startup where the service provider is fully built.
| using (var tempProvider = builder.Services.BuildServiceProvider()) | |
| { | |
| var apiVersionProvider = tempProvider.GetRequiredService<IApiVersionDescriptionProvider>(); | |
| // Configure OpenAPI for each API version | |
| foreach (var description in apiVersionProvider.ApiVersionDescriptions) | |
| { | |
| builder.Services.AddOpenApi(description.GroupName, options => | |
| { | |
| options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1; | |
| options.AddDocumentTransformer(DocumentDefaults.GetDocumentTransformer( | |
| version: description.ApiVersion.ToString())); | |
| options.CreateSchemaReferenceId = (type) => | |
| { | |
| var defaultName = type.Type.Name; | |
| var cleanName = defaultName | |
| .Replace("[]", "Array") | |
| .Replace("`1", "Of") | |
| .Replace("`2", "OfTwo"); | |
| return cleanName; | |
| }; | |
| options.AddOperationTransformer((operation, context, cancellationToken) => | |
| { | |
| var actionDescriptor = context.Description.ActionDescriptor; | |
| // Use endpoint name if available | |
| var endpointName = actionDescriptor.EndpointMetadata | |
| .OfType<EndpointNameMetadata>() | |
| .FirstOrDefault()?.EndpointName; | |
| if (!string.IsNullOrEmpty(endpointName)) | |
| { | |
| operation.OperationId = endpointName; | |
| return Task.CompletedTask; | |
| } | |
| // For controllers | |
| var controller = actionDescriptor.RouteValues.TryGetValue("controller", out var ctrl) ? ctrl : null; | |
| var action = actionDescriptor.RouteValues.TryGetValue("action", out var act) ? act : null; | |
| if (!string.IsNullOrEmpty(controller) && !string.IsNullOrEmpty(action)) | |
| { | |
| operation.OperationId = $"{controller}{action}"; | |
| } | |
| return Task.CompletedTask; | |
| }); | |
| }); | |
| } | |
| } | |
| // NOTE: | |
| // The previous implementation built a temporary service provider here to resolve | |
| // IApiVersionDescriptionProvider and register an OpenAPI document per API version. | |
| // Building a temporary provider during service registration is an anti-pattern and | |
| // was flagged by static analysis. Per-version OpenAPI document registration should | |
| // be performed in a component that receives IApiVersionDescriptionProvider via DI | |
| // (for example, using IConfigureOptions in another part of the application) rather | |
| // than by calling BuildServiceProvider() on builder.Services. |
| run: | | ||
| set -euo pipefail | ||
| dotnet test -c Release ${{ matrix.project }} | ||
| dotnet test -c Release --project ${{ matrix.project }} |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The --project flag is incorrect syntax for dotnet test. The correct syntax is to pass the project path directly without a flag, as it was before. Change back to: dotnet test -c Release ${{ matrix.project }}
| dotnet test -c Release --project ${{ matrix.project }} | |
| dotnet test -c Release ${{ matrix.project }} |
| var assembly = typeof(TProgram).Assembly; | ||
|
|
||
| string assemblyName = assembly | ||
| .GetName() | ||
| .Name ?? throw new NullReferenceException("Assembly name"); | ||
|
|
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment to assemblyName is useless, since its value is never read.
| var assembly = typeof(TProgram).Assembly; | |
| string assemblyName = assembly | |
| .GetName() | |
| .Name ?? throw new NullReferenceException("Assembly name"); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
To be merged after .NET 10 release date, November 11th 2025
closes #185