From d246603db696f600c08bbf31264b4cbb8372ce79 Mon Sep 17 00:00:00 2001 From: Steven Giesel Date: Tue, 10 Nov 2026 18:27:55 +0100 Subject: [PATCH] chore: Upgrade to .NET 11 with runtime async - Target net11.0, SDK 11.0.100, Docker images and CI on 11.0 - Enable runtime-async for all projects - Bump Microsoft packages to 11.0.0 and other dependencies to latest - Npgsql (11.0.0-rc.3) and MySQL (11.0.0-preview.1) providers are prereleases; NU1608 kept as warning until Npgsql 11 ships - EF Core 11: map Tags via PrimitiveCollection instead of Property - Drop explicit Microsoft.Extensions.Configuration.Abstractions (now pruned) - Fix new CA1307 and BL0012 findings; suppress BL0013, BL0016 and S3881 on Razor-generated code --- .github/workflows/codeql.yml | 2 +- .github/workflows/dotnet.yml | 2 +- Directory.Build.props | 8 +++- Directory.Packages.props | 41 +++++++++---------- Dockerfile | 4 +- global.json | 2 +- .../Sql/Mapping/BlogPostConfiguration.cs | 2 +- .../Mapping/BlogPostVersionConfiguration.cs | 2 +- .../OpenIdConnect/AuthExtensions.cs | 2 +- .../Components/CreateNewBlogPost.razor | 2 - .../Components/VersionDiffDialog.razor | 1 - .../BlogPostEditor/UpdateBlogPostPage.razor | 1 - .../Admin/Sitemap/Services/SitemapService.cs | 2 +- .../Features/Components/ShortBlogPost.razor | 1 - .../ShowBlogPost/ShowBlogPostPage.razor | 1 - .../LinkDotNet.Blog.IntegrationTests.csproj | 1 - 16 files changed, 36 insertions(+), 38 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9d437dcb..0fb70d61 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,7 +31,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x - 10.0.x + 11.0.x - name: Initialize CodeQL uses: github/codeql-action/init@v4.39.1 diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 15474913..e09da473 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -31,7 +31,7 @@ jobs: uses: actions/setup-dotnet@v6 with: dotnet-version: | - 10.0.x + 11.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/Directory.Build.props b/Directory.Build.props index fbaa612a..e0a34346 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,12 +1,15 @@ - net10.0 + net11.0 + $(Features);runtime-async=on enable true + + $(WarningsNotAsErrors);NU1608 @@ -14,6 +17,9 @@ severities configured there never reach diagnostics reported against .razor files, so the Razor generated code needs the suppression here as well. --> $(NoWarn);S8969;S8970 + + $(NoWarn);BL0013;BL0016;S3881 diff --git a/Directory.Packages.props b/Directory.Packages.props index b3c7b0e2..6649d2c9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,45 +9,44 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + - - - + + + - - + + - - + - + - - - + + + - + diff --git a/Dockerfile b/Dockerfile index 6ee9653e..71d0dd3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ -FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:11.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:11.0 AS build WORKDIR /src/LinkDotNet.Blog.Web COPY ./ / RUN dotnet restore "LinkDotNet.Blog.Web.csproj" diff --git a/global.json b/global.json index ceedb4c7..cf105624 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100", + "version": "11.0.100", "rollForward": "latestMinor" }, "test": { diff --git a/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostConfiguration.cs b/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostConfiguration.cs index bdba5a05..cf509c7a 100644 --- a/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostConfiguration.cs +++ b/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostConfiguration.cs @@ -20,7 +20,7 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.Likes).IsRequired(); builder.Property(x => x.IsPublished).IsRequired(); - builder.Property(x => x.Tags).HasMaxLength(2048); + builder.PrimitiveCollection(x => x.Tags).HasMaxLength(2048); builder.Property(x => x.AuthorName).HasMaxLength(256).IsRequired(false); builder.HasIndex(x => new { x.IsPublished, x.UpdatedDate }) diff --git a/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostVersionConfiguration.cs b/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostVersionConfiguration.cs index 4a34dc32..1eabbbca 100644 --- a/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostVersionConfiguration.cs +++ b/src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostVersionConfiguration.cs @@ -20,7 +20,7 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.PreviewImageUrlFallback).HasMaxLength(1024); builder.Property(x => x.Content).IsRequired(); builder.Property(x => x.ShortDescription).IsRequired(); - builder.Property(x => x.Tags).HasMaxLength(2048); + builder.PrimitiveCollection(x => x.Tags).HasMaxLength(2048); builder.Property(x => x.IsPublished).IsRequired(); builder.Property(x => x.ReadingTimeInMinutes).IsRequired(); builder.Property(x => x.AuthorName).HasMaxLength(256).IsRequired(false); diff --git a/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs b/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs index 1d50da49..3f2c5e27 100644 --- a/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs +++ b/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs @@ -65,7 +65,7 @@ private static Task HandleRedirect(AuthInformation auth, RedirectContext context var postLogoutUri = context.Properties.RedirectUri; if (!string.IsNullOrEmpty(postLogoutUri)) { - if (postLogoutUri.StartsWith('/')) + if (postLogoutUri.StartsWith('/', StringComparison.Ordinal)) { var request = context.Request; postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri; diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor index 30733e64..cdd694c4 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor +++ b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor @@ -470,7 +470,6 @@ private void OpenShortCodeDialog() { ShortCodeDialog.Open(); - StateHasChanged(); } private async Task OpenTemplateDialog() @@ -495,7 +494,6 @@ } blogPostTemplates = await TemplateRepository.GetAllAsync(); - StateHasChanged(); } private void LoadTemplate(BlogPostTemplate template) diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/VersionDiffDialog.razor b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/VersionDiffDialog.razor index 29d07daa..ce8c715a 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/VersionDiffDialog.razor +++ b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/VersionDiffDialog.razor @@ -183,7 +183,6 @@ public void Close() { isOpen = false; - StateHasChanged(); } private void Compute() diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/UpdateBlogPostPage.razor b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/UpdateBlogPostPage.razor index 3ca9fdd5..f2d24c27 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/UpdateBlogPostPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/UpdateBlogPostPage.razor @@ -53,6 +53,5 @@ else await BlogPostVersionService.RestoreVersionAsync(blogPostFromDb, version); ToastService.ShowSuccess($"Restored version {version.VersionNumber}"); blogPostFromDb = await BlogPostRepository.GetByIdAsync(BlogPostId); - StateHasChanged(); } } diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/Services/SitemapService.cs b/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/Services/SitemapService.cs index f4771057..4c3bf7f8 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/Services/SitemapService.cs +++ b/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/Services/SitemapService.cs @@ -24,7 +24,7 @@ public async Task CreateSitemapAsync(string baseUri) var urlSet = new SitemapUrlSet(); - if (!baseUri.EndsWith('/')) + if (!baseUri.EndsWith('/', StringComparison.Ordinal)) { baseUri += "/"; } diff --git a/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor b/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor index b1c55e7c..2e4f3994 100644 --- a/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor +++ b/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor @@ -66,7 +66,6 @@ { isBookmarked = !isBookmarked; await BookmarkService.SetBookmark(BlogPost.Id, isBookmarked); - StateHasChanged(); } public override Task SetParametersAsync(ParameterView parameters) diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor index fbc5de25..114c68d6 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor @@ -207,7 +207,6 @@ else if (BlogPost is not null) isBookmarked = !isBookmarked; await BookmarkService.SetBookmark(BlogPost.Id, isBookmarked); - StateHasChanged(); } } diff --git a/tests/LinkDotNet.Blog.IntegrationTests/LinkDotNet.Blog.IntegrationTests.csproj b/tests/LinkDotNet.Blog.IntegrationTests/LinkDotNet.Blog.IntegrationTests.csproj index a6dd4a0b..d740bc12 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/LinkDotNet.Blog.IntegrationTests.csproj +++ b/tests/LinkDotNet.Blog.IntegrationTests/LinkDotNet.Blog.IntegrationTests.csproj @@ -7,7 +7,6 @@ -