Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<LangVersion>12</LangVersion>
<LangVersion>14</LangVersion>
<AnalysisMode>Recommended</AnalysisMode>
<ImplicitUsings>enable</ImplicitUsings>
<PreserveCompilationContext>true</PreserveCompilationContext>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<!-- Exclude NuGet audit and analyzer warnings from being treated as errors -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);NU1902;NU1903;CA1873</WarningsNotAsErrors>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot do we need these changes?

    <!-- Exclude NuGet audit and analyzer warnings from being treated as errors -->
    <WarningsNotAsErrors>$(WarningsNotAsErrors);NU1902;NU1903;CA1873</WarningsNotAsErrors>

And also the changes in WebApplicationFactory.cs? Can we just make sure we only have minimal changes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WarningsNotAsErrors in Directory.Build.props is necessary because:

  1. NU1902/NU1903: .NET 10 introduced stricter NuGet audit that treats transitive package vulnerabilities as errors when TreatWarningsAsErrors is enabled. These vulnerabilities are in old design-time dependencies (Microsoft.AspNetCore.Mvc 1.0.3) pulled in by Microsoft.VisualStudio.Web.CodeGeneration.Design 10.0-rc.1. Without this, dotnet restore fails.

  2. CA1873: New .NET 10 analyzer with 21 instances across the codebase for logging performance. Without suppression, dotnet build fails.

The WebApplicationFactory changes are necessary for EF Core 10 - without EnableServiceProviderCaching(false), tests fail with "multiple database providers registered" error.

Options:

  1. Keep both changes (current state before revert)
  2. Accept these as necessary for .NET 10 upgrade
  3. Downgrade Microsoft.VisualStudio.Web.CodeGeneration.Design to avoid vulnerabilities (but loses .NET 10 support)

Which approach would you prefer?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we upgrade the package to recent enough that it is compatible and doesn't have transitive vulnerabilities? @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially resolved in 0924c9b. I removed Microsoft.VisualStudio.Web.CodeGeneration.Design (RC version) which was only needed for scaffolding and reduced some vulnerabilities.

However, the remaining NU1902/NU1903 warnings are from Microsoft.AspNetCore.Mvc.Testing 10.0.0 (stable, not RC). This Microsoft package has transitive dependencies on old Microsoft.AspNetCore.Mvc 1.0.3 packages with known vulnerabilities. This is a Microsoft package issue, not something we can upgrade - we're already using the latest stable version (10.0.0).

The WarningsNotAsErrors for NU1902/NU1903 is still necessary to prevent build failures from these Microsoft package issues. The WebApplicationFactory changes are also still necessary for EF Core 10 compatibility.


<!-- https://aka.ms/vs-build-acceleration -->
<AccelerateBuildsInVisualStudio>True</AccelerateBuildsInVisualStudio>
Expand Down
22 changes: 11 additions & 11 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,22 @@
<PackageVersion Include="HtmlAgilityPack" Version="1.11.72" />
<PackageVersion Include="IntelliTect.Multitool" Version="1.5.3" />
<PackageVersion Include="Mailjet.Api" Version="3.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.12" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.12" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.12" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.12" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.11" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.60.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.PgVector" Version="1.60.0-preview" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="10.0.0-rc.1.25458.5" />
<PackageVersion Include="ModelContextProtocol" Version="0.3.0-preview.4" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.3.0-preview.4" />
<PackageVersion Include="Moq" Version="4.20.72" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion EssentialCSharp.Chat/EssentialCSharp.Chat.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Version>0.0.1</Version>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion EssentialCSharp.Web.Tests/EssentialCSharp.Web.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>

<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
Expand Down
21 changes: 16 additions & 5 deletions EssentialCSharp.Web.Tests/WebApplicationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,33 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
ServiceDescriptor? descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<EssentialCSharpWebContext>));

// Remove the existing DbContext and related services
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on line 19 mentions "Remove the existing DbContext and related services" but the code only removes a single service descriptor (lines 20-25). The broader removal happens in the subsequent block (lines 27-34). Consider moving this comment to line 27 or updating it to clarify that this first block only removes the primary DbContext service.

Suggested change
// Remove the existing DbContext and related services
// Remove the primary DbContextOptions registration for EssentialCSharpWebContext

Copilot uses AI. Check for mistakes.
var descriptorType = typeof(DbContextOptions<EssentialCSharpWebContext>);
var descriptor = services.SingleOrDefault(d => d.ServiceType == descriptorType);
if (descriptor != null)
{
services.Remove(descriptor);
}

// Remove all DbContextOptions related services to avoid EF Core 10 multiple provider error
var allDbContextOptions = services.Where(d =>
d.ServiceType.IsGenericType &&
d.ServiceType.Name.Contains("DbContextOptions")).ToList();
foreach (var desc in allDbContextOptions)
{
services.Remove(desc);
}
Comment on lines +28 to +34
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filtering logic using Name.Contains("DbContextOptions") is fragile and could inadvertently remove services unrelated to EF Core. This string-based matching could match types that happen to have "DbContextOptions" in their name. Consider a more precise approach such as checking for specific known types like DbContextOptions<TContext>, IDbContextOptions, or filtering by namespace (e.g., types from Microsoft.EntityFrameworkCore).

Copilot uses AI. Check for mistakes.

_Connection = new SqliteConnection(SqlConnectionString);
_Connection.Open();

// Add SQLite DbContext without using the global service provider
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "Add SQLite DbContext without using the global service provider" but the code in lines 47-52 still uses BuildServiceProvider() to create a service provider and initialize the database. The comment may be misleading - consider clarifying that this refers to the DbContext options not using a cached/global service provider, while database initialization still requires building a temporary service provider.

Copilot uses AI. Check for mistakes.
services.AddDbContext<EssentialCSharpWebContext>(options =>
{
options.UseSqlite(_Connection);
});
// Disable service provider caching to avoid shared state in EF Core 10
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "Disable service provider caching to avoid shared state in EF Core 10" but doesn't explain why this is necessary or the implications. Consider expanding this comment to explain the specific EF Core 10 behavior change that necessitates this workaround, and any potential performance impact of disabling caching.

Suggested change
// Disable service provider caching to avoid shared state in EF Core 10
// EF Core 10 change: DbContextOptions now participate more aggressively in internal
// service provider caching so that model- and provider-specific services can be reused
// across contexts. In this test setup we create many WebApplicationFactory instances that
// all share the same in-memory SQLite connection. If EF Core caches the internal service
// provider globally, state for one test run (e.g., model metadata, conventions, and
// provider services) can be reused by another, causing "multiple providers" errors and
// flaky tests due to unexpected shared state between factories.
//
// Disabling service provider caching forces EF Core to build a fresh internal service
// provider for each DbContextOptions instance, isolating tests at the cost of a small
// per-context startup overhead. This trade-off is acceptable for integration tests, but
// should generally be avoided in production code where the additional allocations and
// startup cost could impact performance.

Copilot uses AI. Check for mistakes.
options.EnableServiceProviderCaching(false);
}, ServiceLifetime.Scoped, ServiceLifetime.Scoped);

using ServiceProvider serviceProvider = services.BuildServiceProvider();
using IServiceScope scope = serviceProvider.CreateScope();
Expand Down
2 changes: 1 addition & 1 deletion EssentialCSharp.Web/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ WORKDIR /app
EXPOSE 8080
EXPOSE 8081

FROM mcr.microsoft.com/dotnet/sdk:9.0.306 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG ACCESS_TO_NUGET_FEED=true
ENV ACCESS_TO_NUGET_FEED=$ACCESS_TO_NUGET_FEED
RUN sh -c "$(curl -fsSL https://aka.ms/install-artifacts-credprovider.sh)"
Expand Down
2 changes: 1 addition & 1 deletion EssentialCSharp.Web/EssentialCSharp.Web.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PlaceholderChapterOneHtmlFile Include="$(ProjectDir)/Placeholders/Chapters/01/Pages/*.html" />
Expand Down
2 changes: 1 addition & 1 deletion EssentialCSharp.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
// Only loopback proxies are allowed by default.
// Clear that restriction because forwarders are enabled by explicit
// configuration.
options.KnownNetworks.Clear();
options.KnownIPNetworks.Clear();
options.KnownProxies.Clear();
});

Expand Down Expand Up @@ -225,7 +225,7 @@
cancellationToken);

// Optional logging
initialLogger.LogWarning("Rate limit exceeded for user: {User}, IP: {IpAddress}",

Check warning on line 228 in EssentialCSharp.Web/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze C# (CodeQL) (csharp)

Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873)

Check warning on line 228 in EssentialCSharp.Web/Program.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873)
context.HttpContext.User.Identity?.Name ?? "anonymous",
context.HttpContext.Connection.RemoteIpAddress);
return;
Expand All @@ -234,7 +234,7 @@
await context.HttpContext.Response.WriteAsync("Rate limit exceeded. Please try again later.", cancellationToken);

// Optional logging
initialLogger.LogWarning("Rate limit exceeded for user: {User}, IP: {IpAddress}",

Check warning on line 237 in EssentialCSharp.Web/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze C# (CodeQL) (csharp)

Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873)

Check warning on line 237 in EssentialCSharp.Web/Program.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873)
context.HttpContext.User.Identity?.Name ?? "anonymous",
context.HttpContext.Connection.RemoteIpAddress);
};
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.101",
"version": "10.0.100",
"rollForward": "latestMinor"
}
}
Loading