From b99bb1995ca059d22567c7591a1fd99a90e9b89d Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Wed, 19 Aug 2026 13:14:31 +0200 Subject: [PATCH] JoinCollectionsAggregationPipeline: retarget net10.0, bump MongoDB.Driver 3.10.0, fix equality contract - Retarget both projects to net10.0. - MongoDB.Driver 2.22.0 -> 3.10.0; the four-string Lookup() overload the sample uses is unchanged at that version. - Testcontainers.MongoDb 3.6.0 -> 4.14.0. The old version dragged in SSH.NET 2023.0.0 (NU1903, high severity) and BouncyCastle 2.2.1 (NU1902); 4.x drops both. The parameterless MongoDbBuilder ctor is obsolete in 4.x, so the image is now pinned explicitly to mongo:8.0. - Test packages bumped to the versions the recently retargeted samples use (Microsoft.NET.Test.Sdk 18.9.0, xunit 2.9.3, xunit.runner.visualstudio 2.8.2, coverlet.collector 10.0.1). - StudentRepository.GetAllUsers() -> GetAllStudentsAsync(). Nothing in this sample is a user, and the method is async. - Student.Equals compared course lists with All(...ElementAt(IndexOf(...))), which throws ArgumentOutOfRangeException when the other student has fewer courses and is O(n^2). Replaced with SequenceEqual, which uses the Course.Equals override already present. - Equality contract: Course.GetHashCode combined Id while Course.Equals ignores it, and Student had the same asymmetry. Id is now out of both hash codes, and Student's hash walks StudentCourses so equal students hash equally instead of hashing the list by reference. - Dropped the five [Required] DataAnnotations from Course and Student, plus the now-unused using. The MongoDB driver does not read DataAnnotations and this is a console app with no model binding, so they implied a validation guarantee that never existed. Build and test verified on net10.0 (SDK 10.0.302), 0 warnings, 0 errors. --- .../AggregationPipelineLiveTest.cs | 4 +- ...ollectionsAggregationPipeline.Tests.csproj | 12 ++--- .../JoinCollectionsAggregationPipeline.csproj | 6 +-- .../Models/Course.cs | 17 +++---- .../Models/Student.cs | 46 ++++++++++--------- .../Program.cs | 8 ++-- .../StudentRepository.cs | 2 +- 7 files changed, 47 insertions(+), 48 deletions(-) diff --git a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/AggregationPipelineLiveTest.cs b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/AggregationPipelineLiveTest.cs index 6ddfebe944..cc2c638b92 100644 --- a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/AggregationPipelineLiveTest.cs +++ b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/AggregationPipelineLiveTest.cs @@ -3,7 +3,7 @@ public class AggregationPipelineLiveTest : IAsyncLifetime { private readonly MongoDbContainer _mongoDbContainer = - new MongoDbBuilder().Build(); + new MongoDbBuilder("mongo:8.0").Build(); [Fact] public async Task GivenIHaveUsersAndRolesCollectionsInMongoDB_WhenICallTheGetUserModelsMethod_ThenItMergesTheTwoCollectionsIntoOneResult() @@ -30,7 +30,7 @@ public async Task GivenIHaveUsersAndRolesCollectionsInMongoDB_WhenICallTheGetUse await MongoHelper.AddSeedData(database); //Act - var actualResult = await sut.GetAllUsers(); + var actualResult = await sut.GetAllStudentsAsync(); //Assert Assert.NotNull(actualResult); diff --git a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/JoinCollectionsAggregationPipeline.Tests.csproj b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/JoinCollectionsAggregationPipeline.Tests.csproj index 1f578ee040..23889a0669 100644 --- a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/JoinCollectionsAggregationPipeline.Tests.csproj +++ b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.Tests/JoinCollectionsAggregationPipeline.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable @@ -10,11 +10,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.csproj b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.csproj index c158bb110e..9ee4c76288 100644 --- a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.csproj +++ b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline.csproj @@ -2,14 +2,14 @@ Exe - net8.0 + net10.0 enable enable - - + + diff --git a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Course.cs b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Course.cs index 7b1aad2c79..96856b6c80 100644 --- a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Course.cs +++ b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Course.cs @@ -1,32 +1,29 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -using System.ComponentModel.DataAnnotations; namespace JoinCollectionsAggregationPipeline.Models; public class Course { [BsonElement("_id")] - [BsonRepresentation(BsonType.ObjectId)] - public string Id { get; set; } = string.Empty; + [BsonRepresentation(BsonType.ObjectId)] + public string Id { get; set; } = string.Empty; - [Required] - [BsonElement("Name")] + [BsonElement("Name")] public string Name { get; set; } = string.Empty; - [Required] [BsonElement("Code")] - public string Code { get; set; } = string.Empty; + public string Code { get; set; } = string.Empty; public override bool Equals(object? obj) { - if (obj is not Course course) return false; + if (obj is not Course course) return false; return Name == course.Name && Code == course.Code; } public override int GetHashCode() { - return HashCode.Combine(Id, Name, Code); + return HashCode.Combine(Name, Code); } -} \ No newline at end of file +} diff --git a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Student.cs b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Student.cs index b03d177b99..f56dabc7be 100644 --- a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Student.cs +++ b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Models/Student.cs @@ -1,41 +1,43 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson; -using System.ComponentModel.DataAnnotations; namespace JoinCollectionsAggregationPipeline.Models; -public class Student { - +public class Student +{ [BsonElement("_id")] - [BsonRepresentation(BsonType.ObjectId)] - public string Id { get; set; } = string.Empty; + [BsonRepresentation(BsonType.ObjectId)] + public string Id { get; set; } = string.Empty; - [Required] - [BsonElement("FirstName")] - public string FirstName { get; set; } = string.Empty; + [BsonElement("FirstName")] + public string FirstName { get; set; } = string.Empty; - [Required] - [BsonElement("LastName")] + [BsonElement("LastName")] public string LastName { get; set; } = string.Empty; - [Required] [BsonElement("Major")] public string Major { get; set; } = string.Empty; - [BsonElement("StudentCourses")] - public List StudentCourses { get; set; } = new(); + [BsonElement("StudentCourses")] + public List StudentCourses { get; set; } = []; - public override bool Equals(object? obj) - { - if (obj is not Student student) return false; - return FirstName == student.FirstName && - LastName == student.LastName - && StudentCourses.All(course => course - .Equals(student.StudentCourses.ElementAt(StudentCourses.IndexOf(course)))); + public override bool Equals(object? obj) + { + if (obj is not Student student) return false; + return FirstName == student.FirstName + && LastName == student.LastName + && StudentCourses.SequenceEqual(student.StudentCourses); } public override int GetHashCode() { - return HashCode.Combine(Id, FirstName, LastName, Major, StudentCourses); + var hash = new HashCode(); + hash.Add(FirstName); + hash.Add(LastName); + + foreach (var course in StudentCourses) + hash.Add(course); + + return hash.ToHashCode(); } -} \ No newline at end of file +} diff --git a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Program.cs b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Program.cs index a1023d5340..400dc45abb 100644 --- a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Program.cs +++ b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/Program.cs @@ -4,7 +4,7 @@ using Testcontainers.MongoDb; using JoinCollectionsAggregationPipeline.Models; -await using var mongoDbContainer = new MongoDbBuilder().Build(); +await using var mongoDbContainer = new MongoDbBuilder("mongo:8.0").Build(); await mongoDbContainer.StartAsync(); var mongoClient = new MongoClient(mongoDbContainer.GetConnectionString()); @@ -14,10 +14,10 @@ var database = mongoClient.GetDatabase(DatabaseConfiguration.DatabaseName); await MongoHelper.AddSeedData(database); -var users = await repository.GetAllUsers(); -foreach (var user in users) +var students = await repository.GetAllStudentsAsync(); +foreach (var student in students) { - Console.WriteLine(user.ToJson()); + Console.WriteLine(student.ToJson()); } await mongoDbContainer.StopAsync(); \ No newline at end of file diff --git a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/StudentRepository.cs b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/StudentRepository.cs index 0f56334c2d..524db5e4bf 100644 --- a/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/StudentRepository.cs +++ b/dotnet-mongo-db/JoinCollectionsAggregationPipeline/JoinCollectionsAggregationPipeline/StudentRepository.cs @@ -14,7 +14,7 @@ public StudentRepository(MongoClient client) _studentCollection = database.GetCollection("Students"); } - public async Task> GetAllUsers() + public async Task> GetAllStudentsAsync() { //Empty Pipeline var studentAggregationPipeline = _studentCollection.Aggregate();