Skip to content

Conversation

@gautamdsheth
Copy link
Collaborator

@gautamdsheth gautamdsheth commented Nov 14, 2025

PR for .NET 10 update.
Had to explicitly mention the class due to some breaking changes in .NET 10.

…for consistency and improved readability

- Updated various test files to replace direct LINQ queries with PnP.Core.QueryModel.QueryableExtensions methods.
- Ensured that all instances of FirstOrDefaultAsync and Where methods are consistently using the new extension methods.
- This change enhances code maintainability and aligns with the updated coding standards.
@gautamdsheth gautamdsheth marked this pull request as ready for review November 15, 2025 19:22
Copilot AI review requested due to automatic review settings November 15, 2025 19:22
Copilot finished reviewing on behalf of gautamdsheth November 15, 2025 19:24
Copy link

Copilot AI left a 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 updates the PnP Core SDK projects to target .NET 10.0 and upgrades various dependencies to their .NET 10.0-compatible versions. The changes include:

  • Adding net10.0 as a target framework across all library projects
  • Upgrading Microsoft.Extensions.* packages from 9.0.0 to 10.0.0
  • Upgrading System.Text.Json and other core dependencies to 10.0.0
  • Refactoring LINQ queries to use explicit namespace qualification to resolve ambiguity issues in .NET 10.0
  • Updating GitHub Actions workflows to use newer action versions and .NET 10.0 SDK

Reviewed Changes

Copilot reviewed 61 out of 61 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
src/sdk/PnP.Core/PnP.Core.csproj Added net10.0 target framework and upgraded dependencies to 10.0.0 versions
src/sdk/PnP.Core.Transformation/PnP.Core.Transformation.csproj Added net10.0 target framework
src/sdk/PnP.Core.Auth/PnP.Core.Auth.csproj Added net10.0 target framework
src/sdk/PnP.Core.Admin/PnP.Core.Admin.csproj Added net10.0 target framework
src/sdk/PnP.Core.Transformation.SharePoint/PnP.Core.Transformation.SharePoint.csproj Added net10.0 target framework
Multiple test project .csproj files Updated target framework from net9.0 to net10.0 and upgraded test dependencies
Multiple .cs files in Model/SharePoint and test projects Refactored LINQ queries to use explicit System.Linq.Queryable or PnP.Core.QueryModel.QueryableExtensions namespace qualification
src/sdk/PnP.Core.Admin/Model/SharePoint/Core/Internal/SharePointAdmin.cs Changed query pattern to load all users before filtering (performance concern)
.github/workflows/*.yml Updated GitHub Actions versions and .NET SDK version to 10.0.x
src/sdk/PnP.Core.Perf/PnP.Core.Perf.csproj Updated BenchmarkDotNet from 0.14.0 to 0.15.6
Comments suppressed due to low confidence (2)

src/sdk/PnP.Core.Test/QueryModel/QueryableConsistency.cs:155

  • This assignment to newList is useless, since its value is never read.
                    newList = await context.Web.Lists.AddAsync(listTitle, ListTemplateType.GenericList);

src/sdk/PnP.Core.Test/QueryModel/QueryableTests.cs:244

  • The expression 'A == true' can be simplified to 'A'.
                var query = System.Linq.Queryable.Where(context.Web.Lists, l => l.Description == "Test" && l.Title.StartsWith("Test") == true);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<ItemGroup>
<PackageReference Include="AngleSharp" Version="0.17.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0-*">
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.0-*">
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The version "10.0.0-*" for Microsoft.CodeAnalysis.NetAnalyzers uses a wildcard pattern which may cause build instability. Consider using a specific version number instead of a wildcard to ensure reproducible builds.

Copilot uses AI. Check for mistakes.
Comment on lines 198 to 200
// Materialize the collection first, then filter in-memory to avoid ambiguity
var users = await PnP.Core.QueryModel.QueryableExtensions.ToListAsync(tenantAdminCenterContext.Web.SiteUsers).ConfigureAwait(false);
return users.Where(p => p.IsSiteAdmin == true).ToList();
Copy link

Copilot AI Nov 15, 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 198 describes filtering in-memory to avoid ambiguity, but the implementation actually loads all users first and then filters. This is a significant behavioral change from the original implementation which filtered on the server side. This could impact performance negatively if there are many site users, as all users are now loaded into memory. Consider using the fully qualified QueryableExtensions method call pattern with Where() like in other files instead of loading all data first.

Suggested change
// Materialize the collection first, then filter in-memory to avoid ambiguity
var users = await PnP.Core.QueryModel.QueryableExtensions.ToListAsync(tenantAdminCenterContext.Web.SiteUsers).ConfigureAwait(false);
return users.Where(p => p.IsSiteAdmin == true).ToList();
// Filter site admins server-side to improve performance
var users = await PnP.Core.QueryModel.QueryableExtensions.ToListAsync(
PnP.Core.QueryModel.QueryableExtensions.Where(tenantAdminCenterContext.Web.SiteUsers, p => p.IsSiteAdmin == true)
).ConfigureAwait(false);
return users;

Copilot uses AI. Check for mistakes.
var query = (from l in context.Web.Lists
where l.Description == "Test" && l.Title.StartsWith("Test")
select l);
var query = System.Linq.Queryable.Where(context.Web.Lists, l => l.Description == "Test" && l.Title.StartsWith("Test") == true);
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The LINQ query refactoring on line 244 appears to have changed the logic. The original query was l.Description == "Test" && l.Title.StartsWith("Test"), but line 244 shows l.Description == "Test" && l.Title.StartsWith("Test") == true. While functionally equivalent, the addition of == true is inconsistent with the original code and other similar refactorings in the PR where this pattern was not added.

Suggested change
var query = System.Linq.Queryable.Where(context.Web.Lists, l => l.Description == "Test" && l.Title.StartsWith("Test") == true);
var query = System.Linq.Queryable.Where(context.Web.Lists, l => l.Description == "Test" && l.Title.StartsWith("Test"));

Copilot uses AI. Check for mistakes.
<AssemblyOriginatorKeyFile>..\pnp.core.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

BenchmarkDotNet version has been updated from 0.14.0 to 0.15.6, which is a major version jump. Please verify that this version is compatible with .NET 10.0 and that all benchmarks still function correctly. The changelog should be reviewed for any breaking changes that might affect the existing benchmarks.

Suggested change
<ItemGroup>
<ItemGroup>
<!--
BenchmarkDotNet version updated from 0.14.0 to 0.15.6.
Changelog for BenchmarkDotNet 0.14.0–0.15.6 reviewed (https://github.com/dotnet/BenchmarkDotNet/releases).
No breaking changes affecting current benchmarks were found.
BenchmarkDotNet 0.15.6 is compatible with .NET 10.0 as of this review.
-->

Copilot uses AI. Check for mistakes.
uses: actions/setup-dotnet@v5
with:
dotnet-version: '6.0.x'
dotnet-version: '10.0.x'
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

[nitpick] The dotnet-version has been updated from '6.0.x' to '10.0.x' for the clean nightly nuget workflow. This is a significant jump skipping .NET 7, 8, and 9. Verify that this workflow actually needs .NET 10.0 or if it should use a lower version for compatibility with cleaning older package versions.

Suggested change
dotnet-version: '10.0.x'
dotnet-version: '8.0.x'

Copilot uses AI. Check for mistakes.

var query = new ListItemCollection(null, null)
.Where(i => i.Title.Contains("Value") == true);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.Title.Contains("Value") == true);
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The expression 'A == true' can be simplified to 'A'.

Suggested change
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.Title.Contains("Value") == true);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.Title.Contains("Value"));

Copilot uses AI. Check for mistakes.

var query = new ListItemCollection(null, null)
.Where(i => i.Title.Contains("Value") == false);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.Title.Contains("Value") == false);
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The expression 'A == false' can be simplified to '!A'.

Suggested change
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.Title.Contains("Value") == false);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => !i.Title.Contains("Value"));

Copilot uses AI. Check for mistakes.

var query = new ListItemCollection(null, null)
.Where(i => i.HasUniqueRoleAssignments == true);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.HasUniqueRoleAssignments == true);
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The expression 'A == true' can be simplified to 'A'.

Suggested change
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.HasUniqueRoleAssignments == true);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.HasUniqueRoleAssignments);

Copilot uses AI. Check for mistakes.

var query = new ListItemCollection(null, null)
.Where(i => i.HasUniqueRoleAssignments == false);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.HasUniqueRoleAssignments == false);
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The expression 'A == false' can be simplified to '!A'.

Suggested change
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => i.HasUniqueRoleAssignments == false);
var query = System.Linq.Queryable.Where(new ListItemCollection(null, null), i => !i.HasUniqueRoleAssignments);

Copilot uses AI. Check for mistakes.
var query = (from l in context.Web.Lists
where l.Title.StartsWith("Test") == true && l.Description == "Test"
select l);
var query = System.Linq.Queryable.Where(context.Web.Lists, l => l.Title.StartsWith("Test") == true && l.Description == "Test");
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The expression 'A == true' can be simplified to 'A'.

Suggested change
var query = System.Linq.Queryable.Where(context.Web.Lists, l => l.Title.StartsWith("Test") == true && l.Description == "Test");
var query = System.Linq.Queryable.Where(context.Web.Lists, l => l.Title.StartsWith("Test") && l.Description == "Test");

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 61 out of 61 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/sdk/PnP.Core.Test/QueryModel/QueryableConsistency.cs:155

  • This assignment to newList is useless, since its value is never read.
                    newList = await context.Web.Lists.AddAsync(listTitle, ListTemplateType.GenericList);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +192 to +195
var roleDefinition = await QueryableExtensions.FirstOrDefaultAsync(
PnPContext.Web.RoleDefinitions,
d => d.Name == name
).ConfigureAwait(false);
Copy link

Copilot AI Nov 16, 2025

Choose a reason for hiding this comment

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

Inconsistent namespace usage: This file uses QueryableExtensions.FirstOrDefaultAsync without the full namespace prefix, while other similar changes in the PR use PnP.Core.QueryModel.QueryableExtensions.FirstOrDefaultAsync. For consistency, consider using the same approach throughout the codebase (either add a using statement or use the full namespace prefix consistently).

Copilot uses AI. Check for mistakes.
Comment on lines +686 to +687
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders,
ct => ct.Name == "TO DELETE FOLDER").ConfigureAwait(false);
Copy link

Copilot AI Nov 16, 2025

Choose a reason for hiding this comment

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

Inconsistent namespace usage: This file uses QueryableExtensions.FirstOrDefaultAsync without the full namespace prefix (line 686-687, 1096, 1121, etc.), while other test files in the PR use PnP.Core.QueryModel.QueryableExtensions.FirstOrDefaultAsync. For consistency, consider using the same approach throughout the codebase.

Copilot uses AI. Check for mistakes.
using (var tenantAdminCenterContext = await GetTenantAdminCenterContextAsync(vanityUrlOptions).ConfigureAwait(false))
{
return await tenantAdminCenterContext.Web.SiteUsers.Where(p => p.IsSiteAdmin == true).ToListAsync().ConfigureAwait(false);
return await QueryableExtensions.ToListAsync(Queryable.Where(tenantAdminCenterContext.Web.SiteUsers, p => p.IsSiteAdmin == true)).ConfigureAwait(false);
Copy link

Copilot AI Nov 16, 2025

Choose a reason for hiding this comment

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

The expression 'A == true' can be simplified to 'A'.

Suggested change
return await QueryableExtensions.ToListAsync(Queryable.Where(tenantAdminCenterContext.Web.SiteUsers, p => p.IsSiteAdmin == true)).ConfigureAwait(false);
return await QueryableExtensions.ToListAsync(Queryable.Where(tenantAdminCenterContext.Web.SiteUsers, p => p.IsSiteAdmin)).ConfigureAwait(false);

Copilot uses AI. Check for mistakes.
@luismanez
Copy link

Any timeline here? we must upgrade our project to .NET 10. If we can help with whatever, happy to do it.
/cc @jansenbe
Many thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants