Skip to content

Rebuild graph update system with contract-driven relationship semantics v2 rebuild: EF Core 10 targeting with modular refactoring @coderabbitai#16

Merged
WahidBitar merged 16 commits into
masterfrom
001-rebuild-graph-update
Apr 5, 2026
Merged

Rebuild graph update system with contract-driven relationship semantics v2 rebuild: EF Core 10 targeting with modular refactoring @coderabbitai#16
WahidBitar merged 16 commits into
masterfrom
001-rebuild-graph-update

Conversation

@WahidBitar
Copy link
Copy Markdown
Owner

@WahidBitar WahidBitar commented Apr 5, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Updated graph update API (UpdateGraph) with explicit support for pure and payload many-to-many, and required/optional one-to-one relationships.
    • New validation exceptions for unsupported relationship patterns and mutation attempts.
  • Chores

    • Upgraded to .NET 10 and Entity Framework Core 10.x.

WahidBitar and others added 16 commits March 31, 2026 13:43
Update actions/checkout from v2 to v4, actions/setup-dotnet from v1 to v4, and change dotnet-version to "10.0.x". Also fix indentation and remove extra spaces in branch names.
Add a new publish job to the CI pipeline that builds, packs, and publishes the NuGet package on master pushes, with conditional checks for API key availability. Update action versions (checkout@v6, setup-dotnet@v5), adjust commands to target the solution file, and refine job names and permissions.

BREAKING CHANGE: package version bumped from 2.0.0 to 10.0.0 to align with EF Core 10+ targeting, which may introduce incompatibilities.
Docstrings generation was requested by @WahidBitar.

* #13 (comment)

The following files were modified:

* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/CourseConfiguration.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/CourseMentorAssignmentConfiguration.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/CoursePolicyConfiguration.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/LearningCatalogConfiguration.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/MentorConfiguration.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/MentorWorkspaceConfiguration.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/TopicTagConfiguration.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.TestModel/TestDbContext.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrap.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedData.cs`
* `src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SqlServerContainerFixture.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/DbContextExtensions.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/Exceptions/AmbiguousOwnershipSemanticsException.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/Exceptions/GraphUpdateException.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/Exceptions/PartialMutationNotAllowedException.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnloadedNavigationMutationException.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnsupportedNavigationMutatedException.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnsupportedRelationshipPatternException.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/EntityKeyHelper.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/GraphUpdateOrchestrator.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/OperationGuard.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/RelatedEntityMutationService.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OneToOneOwnershipResolver.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OptionalOneToOneStrategy.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/RequiredOneToOneStrategy.cs`
* `src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs`
…and graph update exceptions

Enhance test coverage by adding integration tests for the SeedData helper to validate distinct IDs, entity relationships, and in-memory database seeding. Introduce unit tests for `GraphUpdateException` derived types, ensuring robust constructor validations, message formatting, and inheritance chain testing. Remove deprecated `ManyToManyDiffStrategyTests` in favor of the new testing structure. Update dependencies in project files to include EF Core InMemory provider for test scenarios.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 5, 2026

📝 Walkthrough

Walkthrough

This pull request executes a major v2 rebuild targeting .NET 10 and EF Core 10.x. It replaces the legacy v1 infrastructure with a contract-driven graph update system supporting pure/payload many-to-many and required/optional one-to-one relationships, removes outdated test models, establishes comprehensive test coverage, and updates deployment workflows for the new architecture.

Changes

Cohort / File(s) Summary
Build & Workflow Configuration
.github/workflows/dotnet-core.yml, src/EFCore.UpdateGraph.slnx
Updated GitHub Actions to a two-job pipeline (build on all master commits, publish only on direct pushes to master). Replaced legacy .sln with new .slnx file listing four projects (main lib, test model, unit tests, integration tests).
Project Metadata & Tooling
.gitignore, README.md, src/Diwink.Extensions.EntityFrameworkCore/Diwink.Extensions.EntityFrameworkCore.csproj
Added IDE ignore patterns (.idea/, *.iml). Updated main library .csproj to target net10.0, set version 10.0.0, depend on EF Core 10.0.5, and expose internals to test assemblies. Added v2 usage documentation with relationship patterns and rejection behavior.
Legacy Cleanup
EfCore.UpdateGraph.sln, src/FakeModel/*, src/UnitTests/*
Removed old .NET 3.1 solution definition, entire FakeModel project with eight entity types, and legacy UnitTests project including all migrations, test fixtures (GraphUpdateTests, VariousTests), and configuration helpers.
Core Graph Update Logic
src/Diwink.Extensions.EntityFrameworkCore/DbContextExtensions.cs, src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/*
Replaced single InsertUpdateOrDeleteGraph with new UpdateGraph<T> extension delegating to GraphUpdateOrchestrator. Added GraphUpdateOrchestrator orchestrating two-phase validation and mutation flows, EntityKeyHelper for primary-key extraction/comparison, and OperationGuard enforcing all-or-nothing semantics.
Exception Hierarchy
src/Diwink.Extensions.EntityFrameworkCore/Exceptions/*
Introduced abstract GraphUpdateException base with six concrete sealed subclasses (UnsupportedNavigationMutatedException, UnloadedNavigationMutationException, UnsupportedRelationshipPatternException, PartialMutationNotAllowedException, AmbiguousOwnershipSemanticsException) capturing specific rejection reasons with formatted messages.
One-to-One Strategies
src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OneToOne*.cs, src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/RequiredOneToOneStrategy.cs
Added OneToOneOwnershipResolver to determine required/optional dependents, OptionalOneToOneStrategy for nulling FK on removal and attaching/replacing dependents, and RequiredOneToOneStrategy for cascade delete on removal.
Many-to-Many Strategies
src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs, src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs
Added pure many-to-many (skip navigation) strategy reconciling collection membership via key matching with optional entity tracking/attach-on-add. Added payload-association strategy handling join-table entities with optional property updates.
Navigation & Traversal Utilities
src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs
Added guard utility validating loaded navigations recursively and detecting unloaded mutation attempts with cycle prevention.
Test Model Entities & Configs
src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/*.cs, src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/*.cs
Introduced seven entities (LearningCatalog, Course, CoursePolicy, TopicTag, Mentor, MentorWorkspace, CourseMentorAssignment) and corresponding EF Core IEntityTypeConfiguration implementations defining primary keys, required properties, indexes, and relationship mappings (one-to-many, one-to-one, many-to-many).
Test Model Project
src/Diwink.Extensions.EntityFrameworkCore.TestModel/TestDbContext.cs, src/Diwink.Extensions.EntityFrameworkCore.TestModel/Diwink.Extensions.EntityFrameworkCore.TestModel.csproj
Added TestDbContext with DbSet properties and OnModelCreating applying all entity configurations. Created new test project targeting net10.0 with EF Core SQL Server provider dependency.
Integration Test Infrastructure
src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/*
Added SqlServerContainerFixture for containerized SQL Server lifecycle management, DatabaseBootstrap for schema creation/reset with retry logic, IntegrationTestBase abstract class providing per-test database reset via IAsyncLifetime, IntegrationTestCollection xUnit collection definition binding all integration tests to shared container fixture, and SeedData with deterministic entity IDs and comprehensive scenario seeding method.
Integration Contract Tests
src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/ManyToMany/*.cs, src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/OneToOne/*.cs
Added test suites validating: pure many-to-many add/remove/update, payload-association add/update/remove with link preservation, optional one-to-one null-FK-on-remove/scalar-update/add/replace, required one-to-one cascade-delete/update/add behaviors.
Integration Rejection Tests
src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/Rejection/*.cs
Added test suites validating: unsupported one-to-many mutation rejection with proper exception types, unloaded navigation mutation detection, partial mutation all-or-nothing atomicity, and silent skipping of unchanged unsupported relationships.
Integration Test Project & Seed Validation
src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Diwink.Extensions.EntityFrameworkCore.Tests.Integration.csproj, src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedData*.cs
Created integration test project with xUnit, FluentAssertions, Testcontainers, and EF Core providers. Added SeedDataStructureTests validating deterministic seed data consistency and entity relationship presence.
Unit Test Infrastructure & Core Logic Tests
src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/Diwink.Extensions.EntityFrameworkCore.Tests.Unit.csproj, src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/Exceptions/GraphUpdateExceptionTests.cs, src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/*
Created unit test project targeting net10.0 with xUnit, FluentAssertions, in-memory EF Core. Added exception hierarchy validation tests, DbContextExtensions null-argument validation, EntityKeyHelper byte-array comparison and unmapped-entity handling, OperationGuard single/multi-error throwing behavior.
Unit Tests: Relationship Semantics & Patterns
src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/RelationshipSemantics/OneToOneOwnershipResolverTests.cs, src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/RelationshipClassificationTests.cs, src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/NestedNavigationValidationTests.cs, src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/UnsupportedRelationshipPatternTests.cs
Added tests for required/optional one-to-one deletion/nulling/update/add, payload vs. pure many-to-many classification, nested navigation mutation rejection with path reporting, one-to-many in-place scalar-edit rejection. Includes recursive test models validating nested unsupported navigations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 A hop through the v2 rebuild so bright,
From three-point-one to ten—what a flight!
Graph updates dance, relationships aligned,
With contracts so pure and payloads refined.
Old FakeSchools fade, new seeds take their place,
Integration tests run with Testcontainer grace! 🌱

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 001-rebuild-graph-update

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 20

🧹 Nitpick comments (9)
.github/workflows/dotnet-core.yml (1)

45-46: Consider using the secret directly in the push command.

The API key is exposed as an environment variable at the job level (line 46). While this works, you could use the secret directly in the push step to minimize exposure scope:

- name: Publish to NuGet.org
  if: secrets.NUGET_API_KEY != ''
  run: dotnet nuget push "./artifacts/*.nupkg" --api-key "${{ secrets.NUGET_API_KEY }}" --source "https://api.nuget.org/v3/index.json" --skip-duplicate

This keeps the secret scoped to only the step that needs it rather than the entire job.

Also applies to: 67-69

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/dotnet-core.yml around lines 45 - 46, Move usage of the
NuGet API key out of the job-level env and reference the secret directly in the
publish step: remove or stop setting NUGET_API_KEY at the job env, and in the
"Publish to NuGet.org" step (and the other publish step referenced around lines
67-69) update the run command to pass the key as --api-key "${{
secrets.NUGET_API_KEY }}" and keep the step-level guard if:
secrets.NUGET_API_KEY != '' so the secret is only exposed to that step.
src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnloadedNavigationMutationException.cs (1)

15-24: Missing validation for navigationName parameter.

The relationshipPath is validated via the base class, but navigationName is assigned directly without validation. Other derived exceptions in this hierarchy validate their additional parameters using the shared ValidateAndNormalize helper. Consider validating navigationName for consistency and to prevent invalid exception states.

♻️ Proposed fix to add validation
     public UnloadedNavigationMutationException(
         string relationshipPath,
         string navigationName)
         : base(
             $"Mutation at '{relationshipPath}' depends on navigation '{navigationName}' " +
             "which was not explicitly loaded. The entire operation was rejected without partial apply.",
             relationshipPath)
     {
-        NavigationName = navigationName;
+        NavigationName = ValidateAndNormalize(navigationName, nameof(navigationName), "Navigation name");
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnloadedNavigationMutationException.cs`
around lines 15 - 24, UnloadedNavigationMutationException assigns navigationName
directly without validation; update its constructor to validate and normalize
navigationName using the shared helper (e.g., call ValidateAndNormalize or the
base class helper used by other derived exceptions) before assigning to the
NavigationName property so the parameter is checked/normalized consistently with
relationshipPath and other exceptions in the hierarchy.
src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs (1)

23-25: Method name/docs imply enforcement, but implementation only traverses loaded navigations.

EnsureNavigationsLoaded currently skips unloaded navigations, so the name can be misread as a full validation guarantee. Consider renaming/documenting it as traversal-only, or invoking RequireLoaded where enforcement is intended.

Also applies to: 42-45

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs`
around lines 23 - 25, The method EnsureNavigationsLoaded in NavigationLoadGuard
only traverses navigations that are already loaded but the name implies
enforcement; either rename the method to something like
TraverseLoadedNavigations and update its XML docs to state it only visits loaded
navigations, or change its implementation to call the existing RequireLoaded
helper for each navigation it visits so it actually enforces loading; apply the
same change to the second overload/occurrence referenced around lines 42-45 and
update any callers or docs accordingly to reflect the new behavior or name.
src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs (1)

33-38: Consider indexing by key to avoid repeated linear scans.

This reconcile path currently performs repeated key extraction and list scans. Building key-index maps for existing/updated entities would reduce complexity and improve behavior on larger collections.

Also applies to: 47-51, 76-81

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs`
around lines 33 - 38, The current reconcile loops in PayloadManyToManyStrategy
perform repeated GetKeyValues and linear searches over
updatedItems/existingItems; replace those scans by precomputing key->entity
dictionaries for both collections (use
EntityKeyHelper.GetKeyValues(context.Entry(item)) as the key) before the three
reconcile phases (the removal loop, the addition loop, and the update/merge loop
referenced around the other blocks), then perform O(1) lookups against those
dictionaries instead of calling EntityKeyHelper.FindByKey repeatedly; ensure the
key representation you build is consistent (e.g., join key parts into a single
comparable string or use a tuple) and update the loops to remove from/lookup
into the dictionaries rather than scanning lists.
src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/DbContextExtensionsTests.cs (1)

20-45: Rename test methods to match UpdateGraph API.

The assertions are correct, but method names still reference InsertUpdateOrDeleteGraph, which no longer matches the public method under test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/DbContextExtensionsTests.cs`
around lines 20 - 45, Rename the test methods so their names reference the
current API (UpdateGraph) instead of the old InsertUpdateOrDeleteGraph; e.g.,
rename InsertUpdateOrDeleteGraph_throws_when_context_is_null to
UpdateGraph_throws_when_context_is_null,
InsertUpdateOrDeleteGraph_throws_when_updated_entity_is_null to
UpdateGraph_throws_when_updated_entity_is_null, etc., ensuring all references
and any test attributes (Fact) remain intact and the tests still call
DbContextExtensions.UpdateGraph<T> (or context.UpdateGraph<T>) with the same
assertions.
src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedData.cs (1)

29-30: Guard context before use in SeedFullScenarioAsync.

Adding ThrowIfNull makes failures immediate and clearer for callers.

♻️ Proposed fix
     public static async Task SeedFullScenarioAsync(TestDbContext context)
     {
+        ArgumentNullException.ThrowIfNull(context);
+
         var tag1 = new TopicTag { Id = Tag1Id, Label = "Architecture" };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedData.cs`
around lines 29 - 30, In SeedFullScenarioAsync, guard the incoming TestDbContext
before any use by adding a null-check; call context.ThrowIfNull(nameof(context))
at the top of the method (or if that helper isn't available, throw new
ArgumentNullException(nameof(context))). This ensures SeedFullScenarioAsync
validates the TestDbContext parameter immediately and fails fast with a clear
error.
src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs (1)

21-24: Add a constructor guard for fixture.

This gives a clearer failure mode if fixture injection ever changes or is bypassed in custom setup.

♻️ Proposed fix
     protected IntegrationTestBase(SqlServerContainerFixture fixture)
     {
+        ArgumentNullException.ThrowIfNull(fixture);
         _fixture = fixture;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs`
around lines 21 - 24, The IntegrationTestBase constructor currently assigns the
incoming SqlServerContainerFixture to _fixture without validation; add a null
check in the IntegrationTestBase(SqlServerContainerFixture fixture) constructor
and throw an ArgumentNullException(nameof(fixture)) (or similar) if fixture is
null to provide a clear failure when fixture injection is missing; update the
constructor body in IntegrationTestBase to validate fixture before assigning to
_fixture.
src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrap.cs (1)

121-124: Narrow the retry filter to transient failures only.

InvalidOperationException is too broad here. Deterministic model/configuration bugs will be retried three times and then wrapped as schema-bootstrap failures, which makes red builds slower and harder to diagnose.

Suggested change
-        return exception is SqlException or TimeoutException or InvalidOperationException ||
+        return exception is SqlException or TimeoutException ||
                (cancellationSource.IsCancellationRequested && exception is OperationCanceledException);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrap.cs`
around lines 121 - 124, The retry predicate IsRetryableSchemaException is too
broad because it treats InvalidOperationException as transient; remove
InvalidOperationException from the allowed set and only treat true transient
failures as retryable (keep SqlException and TimeoutException), and preserve the
cancellation check (cancellationSource.IsCancellationRequested && exception is
OperationCanceledException); update IsRetryableSchemaException accordingly so
deterministic model/configuration errors are not retried.
src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/GraphUpdateOrchestrator.cs (1)

151-157: Casting IEnumerable<object> may fail for value-type collections.

The pattern updatedValue is IEnumerable<object> collection relies on covariance, which works for reference types but will fail for collections of value types (e.g., List<int>). If navigation collections could contain value types, this check would incorrectly fall through to line 156.

For EF Core entity navigations, this is typically fine since entities are reference types, but worth noting if the codebase ever supports value-type collections.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/GraphUpdateOrchestrator.cs`
around lines 151 - 157, The check in GraphUpdateOrchestrator that uses
"updatedValue is IEnumerable<object> collection" can fail for value-type
collections; change it to test for the non-generic IEnumerable (e.g., "if
(updatedValue is System.Collections.IEnumerable enumerable)") and determine
whether it has items by iterating (or using foreach to return true on the first
item) so value-type collections (like List<int>) are handled without casting
errors; update the logic around the updatedValue/collection handling in
GraphUpdateOrchestrator to use this non-generic enumerable check and
early-return when an item is found.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/Course.cs`:
- Around line 12-13: The comment and code disagree: the Course class's Policy
property is marked as nullable (CoursePolicy? Policy) while the comment says
"Required one-to-one"; decide which is correct and make them consistent. If the
relationship is required, change the property to non-nullable (CoursePolicy
Policy) and initialize with null! or ensure constructor assignment so the
compiler is satisfied; if it is optional, update the comment to indicate it's
optional/nullable and ensure EF Core configuration (in the Course/CoursePolicy
mapping) matches. Reference: Course.Policy and the CoursePolicy entity.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/ManyToMany/ManyToManySafetyContractTests.cs`:
- Around line 82-85: The test ManyToManySafetyContractTests should also assert
that assignment links were removed: after creating verifyCtx and verifying
mentorCount, load the affected Course(s) including the MentorAssignments
navigation (e.g., via verifyCtx.Courses.Include(c => c.MentorAssignments) or
fetching the specific course by id) and assert that course.MentorAssignments is
empty (Count==0 or .Should().BeEmpty()), ensuring the unlink operation actually
cleared Course.MentorAssignments in addition to preserving Mentor entities.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/Rejection/PartialMutationNotAllowedTests.cs`:
- Around line 14-17: Add the missing xUnit collection attribute to the test
class: add [Collection(IntegrationTestCollection.Name)] directly above the
PartialMutationNotAllowedTests class declaration so the class
(PartialMutationNotAllowedTests) is explicitly placed in the
IntegrationTestCollection (IntegrationTestCollection.Name) rather than relying
on inheritance from IntegrationTestBase.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrap.cs`:
- Around line 18-24: Fix the malformed XML doc comments in DatabaseBootstrap:
remove the nested/duplicate <summary> tags and ensure each summary has one
opening <summary> and one closing </summary> around the intended text (for the
method that "Creates a fresh DbContext..." / the method that "Creates a
TestDbContext configured to use SQL Server..." and the other doc blocks
mentioned). Update the related XML comments (the other blocks at the same spots)
to use a single well-formed <summary> per comment and correct any mismatched
closing tags so the generated XML docs are valid.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs`:
- Around line 41-45: Remove the duplicate XML documentation opening tag in the
summary block for the method in IntegrationTestBase: delete the second
"<summary>" line (the one starting "Creates a new TestDbContext...") so there is
only one opening <summary> and a single closing </summary> for that method's
documentation in IntegrationTestBase.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedData.cs`:
- Around line 23-27: The XML doc comment for the seed routine contains a
duplicated opening <summary> tag; remove the extra "<summary>" so the comment
has a single opening "<summary>" and one closing "</summary>" wrapping the
sentence that begins "Seeds the provided test database..." (locate the
documentation above the SeedData class/method in SeedData.cs). Apply the same
fix to the malformed doc comments in IntegrationTestBase.cs and
DbContextExtensions.cs so each summary block has exactly one opening and one
closing tag.

In `@src/Diwink.Extensions.EntityFrameworkCore/DbContextExtensions.cs`:
- Around line 12-35: The XML documentation for the UpdateGraph<T> method
contains duplicated/overlapping tags; remove the second <summary> block and
consolidate parameter/returns/exception tags into a single coherent XML doc
using the detailed first summary (including supported relationship patterns and
FR references) and keep one set of <param>, <returns>, and <exception> tags;
update the documentation immediately above the UpdateGraph<T> method declaration
so the method has exactly one <summary> and one set of XML doc elements
describing parameters and exceptions.

In `@src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/EntityKeyHelper.cs`:
- Around line 33-36: The XML doc comment for the EntityKeyHelper summary is
malformed (duplicate and unclosed <summary> tag); update the comment above the
EntityKeyHelper (or the method that "Gets the primary key values for a detached
entity") to a single well-formed XML documentation block—e.g. a single
triple-slash /// <summary>Gets the primary key component values for a detached
CLR entity using the provided DbContext's model metadata.</summary>—removing the
duplicate line and ensuring the opening and closing <summary> tags are present
and properly formatted.
- Around line 99-102: The XML doc comment above the method in the
EntityKeyHelper class is malformed: it contains duplicated/incomplete <summary>
tags. Edit the XML comment for the target method in EntityKeyHelper to have a
single well-formed <summary>...</summary> block (and properly close any tags),
and ensure the content reads correctly (e.g., "Finds the first entity in
<paramref name="collection"/> whose primary key components, as determined from
<paramref name="context"/>'s model, match <paramref name="targetKeys"/>.").
Remove the stray or duplicate <summary> line and verify any <param> or other XML
tags are correctly opened and closed.
- Around line 60-63: The XML doc for the method in EntityKeyHelper is malformed:
the <summary> tag is opened twice. Edit the XML comment on the method that
"Compares two key arrays for equality" in class EntityKeyHelper and replace the
second "<summary>" with a closing "</summary>" and ensure the summary text is a
single coherent block describing that the method determines whether two arrays
of primary-key component values are equal element-by-element in order.
- Around line 12-16: The XML doc block in EntityKeyHelper (the summary lines
describing primary key extraction) is malformed because the first <summary> tag
isn't properly closed before a second <summary> begins; fix it by either
consolidating the two lines into a single well-formed <summary>...</summary> or
by closing the first <summary> tag before the second, so the XML comment for the
EntityKeyHelper/GetPrimaryKeyValues (or the surrounding method) is valid and
produces no warnings.

In
`@src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/GraphUpdateOrchestrator.cs`:
- Around line 20-23: The XML documentation comments in the
GraphUpdateOrchestrator class are malformed (unclosed <summary> tags) — update
each triple-slash comment block to use well-formed XML (for example change "///
<summary> ... <summary>" to "/// <summary> ... </summary>") throughout the file,
mirroring the correct pattern used in EntityKeyHelper; check and fix all
occurrences around the GraphUpdateOrchestrator class and its methods (e.g., the
class declaration and the methods referenced in the diff) so every <summary>,
<param>, <returns> or other XML tag is properly opened and closed.
- Around line 68-127: The try block in GraphUpdateOrchestrator.cs is malformed:
ensure the try has a proper opening brace that encloses the subsequent
navigation checks and that the matching closing brace comes immediately before
the finally block that calls recursionPath.Remove(existingEntry.Entity); update
indentation of the code inside the try accordingly so the foreach loops (over
existingEntry.Navigations.Where(...)), the Unsupported/Loaded validation logic
(including calls to ClassifyNavigation, HasMutations, ValidateLoadedChildren)
and the unloaded-navigation checks are all inside the try block and align with
the finally block.

In `@src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/OperationGuard.cs`:
- Around line 25-30: The XML doc comments for the AddError and ThrowIfErrors
methods contain duplicated opening <summary> tags; edit the docblocks in
OperationGuard (methods AddError and ThrowIfErrors) to remove the extra opening
<summary> so each comment uses a single well-formed <summary>...</summary> pair
(and keep the descriptive text intact), ensuring valid XML documentation and
eliminating the compiler warnings.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OneToOneOwnershipResolver.cs`:
- Around line 12-23: The XML doc for IsRequiredDependent has duplicated/nested
<summary> tags and a misplaced <returns> which makes the XML invalid; fix it by
keeping a single <summary> describing the method (e.g., "Determines whether the
dependent end of the specified one-to-one relationship is required.") and a
single <returns> describing the boolean outcome (e.g., "`true` if required;
`false` if optional"), removing the extra/duplicated <summary> block so the XML
doc is well-formed for the IsRequiredDependent method in
OneToOneOwnershipResolver.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OptionalOneToOneStrategy.cs`:
- Around line 14-19: The XML documentation in OptionalOneToOneStrategy has
duplicate opening <summary> tags causing malformed docs; edit each affected doc
block (the summaries describing detaching/clearing the optional dependent
navigation in the OptionalOneToOneStrategy class) to have a single opening
<summary> and a single closing </summary> wrapping the combined description,
removing the extra <summary> tags and ensuring proper XML structure for all
similar blocks (the other two summary blocks in the same class) so the doc
comments are valid.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs`:
- Around line 47-61: updatedItems can contain duplicate association keys which,
if no tracked match exists, will cause multiple AddToCollection calls and PK
conflicts; inside the loop over updatedItems (where
EntityKeyHelper.GetKeyValues, FindInTracked and AddToCollection are used) add a
guard: compute a stable signature for updatedKeys (e.g., join key values or use
a structural comparer) and keep a HashSet of seen signatures; if a signature is
already seen, skip or merge payloads into the already-processed association
instead of calling AddToCollection again; ensure this check runs before calling
FindInTracked/AddToCollection so duplicates are handled deterministically.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs`:
- Around line 86-93: The condition in ApplyValuesIfNotModified incorrectly
includes EntityState.Detached; calling
trackedEntry.CurrentValues.SetValues(updatedEntity) on a detached entry is a
no-op. Update the method ApplyValuesIfNotModified to only call SetValues when
the tracked entry is actually tracked (e.g., when trackedEntry.State is
EntityState.Unchanged) by removing the "EntityState.Detached" branch from the if
condition so SetValues is only invoked for tracked entities (use the
trackedEntry variable and Entry/CurrentValues as in the existing method).

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/RequiredOneToOneStrategy.cs`:
- Around line 13-20: The XML doc for RemoveDependent in RequiredOneToOneStrategy
has a malformed tag: the second "<summary>" should be a closing "</summary>"
which breaks the documentation; update the block for the RemoveDependent method
by replacing that erroneous opening <summary> with </summary> and ensure the
remaining XML tags (parameters and summaries) are properly closed so the doc
comment is well-formed.

In `@src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs`:
- Around line 16-20: In the XML docs for the NavigationLoadGuard class, remove
the duplicated opening <summary> tag occurrences so each documentation block has
a single <summary> ... </summary> pair; specifically delete the extra <summary>
in the class/header doc and the extra <summary> in the other documentation block
within NavigationLoadGuard.cs so the XML is well-formed around the
NavigationLoadGuard documentation.

---

Nitpick comments:
In @.github/workflows/dotnet-core.yml:
- Around line 45-46: Move usage of the NuGet API key out of the job-level env
and reference the secret directly in the publish step: remove or stop setting
NUGET_API_KEY at the job env, and in the "Publish to NuGet.org" step (and the
other publish step referenced around lines 67-69) update the run command to pass
the key as --api-key "${{ secrets.NUGET_API_KEY }}" and keep the step-level
guard if: secrets.NUGET_API_KEY != '' so the secret is only exposed to that
step.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrap.cs`:
- Around line 121-124: The retry predicate IsRetryableSchemaException is too
broad because it treats InvalidOperationException as transient; remove
InvalidOperationException from the allowed set and only treat true transient
failures as retryable (keep SqlException and TimeoutException), and preserve the
cancellation check (cancellationSource.IsCancellationRequested && exception is
OperationCanceledException); update IsRetryableSchemaException accordingly so
deterministic model/configuration errors are not retried.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs`:
- Around line 21-24: The IntegrationTestBase constructor currently assigns the
incoming SqlServerContainerFixture to _fixture without validation; add a null
check in the IntegrationTestBase(SqlServerContainerFixture fixture) constructor
and throw an ArgumentNullException(nameof(fixture)) (or similar) if fixture is
null to provide a clear failure when fixture injection is missing; update the
constructor body in IntegrationTestBase to validate fixture before assigning to
_fixture.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedData.cs`:
- Around line 29-30: In SeedFullScenarioAsync, guard the incoming TestDbContext
before any use by adding a null-check; call context.ThrowIfNull(nameof(context))
at the top of the method (or if that helper isn't available, throw new
ArgumentNullException(nameof(context))). This ensures SeedFullScenarioAsync
validates the TestDbContext parameter immediately and fails fast with a clear
error.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/DbContextExtensionsTests.cs`:
- Around line 20-45: Rename the test methods so their names reference the
current API (UpdateGraph) instead of the old InsertUpdateOrDeleteGraph; e.g.,
rename InsertUpdateOrDeleteGraph_throws_when_context_is_null to
UpdateGraph_throws_when_context_is_null,
InsertUpdateOrDeleteGraph_throws_when_updated_entity_is_null to
UpdateGraph_throws_when_updated_entity_is_null, etc., ensuring all references
and any test attributes (Fact) remain intact and the tests still call
DbContextExtensions.UpdateGraph<T> (or context.UpdateGraph<T>) with the same
assertions.

In
`@src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnloadedNavigationMutationException.cs`:
- Around line 15-24: UnloadedNavigationMutationException assigns navigationName
directly without validation; update its constructor to validate and normalize
navigationName using the shared helper (e.g., call ValidateAndNormalize or the
base class helper used by other derived exceptions) before assigning to the
NavigationName property so the parameter is checked/normalized consistently with
relationshipPath and other exceptions in the hierarchy.

In
`@src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/GraphUpdateOrchestrator.cs`:
- Around line 151-157: The check in GraphUpdateOrchestrator that uses
"updatedValue is IEnumerable<object> collection" can fail for value-type
collections; change it to test for the non-generic IEnumerable (e.g., "if
(updatedValue is System.Collections.IEnumerable enumerable)") and determine
whether it has items by iterating (or using foreach to return true on the first
item) so value-type collections (like List<int>) are handled without casting
errors; update the logic around the updatedValue/collection handling in
GraphUpdateOrchestrator to use this non-generic enumerable check and
early-return when an item is found.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs`:
- Around line 33-38: The current reconcile loops in PayloadManyToManyStrategy
perform repeated GetKeyValues and linear searches over
updatedItems/existingItems; replace those scans by precomputing key->entity
dictionaries for both collections (use
EntityKeyHelper.GetKeyValues(context.Entry(item)) as the key) before the three
reconcile phases (the removal loop, the addition loop, and the update/merge loop
referenced around the other blocks), then perform O(1) lookups against those
dictionaries instead of calling EntityKeyHelper.FindByKey repeatedly; ensure the
key representation you build is consistent (e.g., join key parts into a single
comparable string or use a tuple) and update the loops to remove from/lookup
into the dictionaries rather than scanning lists.

In `@src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs`:
- Around line 23-25: The method EnsureNavigationsLoaded in NavigationLoadGuard
only traverses navigations that are already loaded but the name implies
enforcement; either rename the method to something like
TraverseLoadedNavigations and update its XML docs to state it only visits loaded
navigations, or change its implementation to call the existing RequireLoaded
helper for each navigation it visits so it actually enforces loading; apply the
same change to the second overload/occurrence referenced around lines 42-45 and
update any callers or docs accordingly to reflect the new behavior or name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eac5858a-4077-4da8-9c5a-d05d249784ce

📥 Commits

Reviewing files that changed from the base of the PR and between f71a103 and 85a001f.

📒 Files selected for processing (77)
  • .github/workflows/dotnet-core.yml
  • .gitignore
  • EfCore.UpdateGraph.sln
  • README.md
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/CourseConfiguration.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/CourseMentorAssignmentConfiguration.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/CoursePolicyConfiguration.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/LearningCatalogConfiguration.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/MentorConfiguration.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/MentorWorkspaceConfiguration.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Configurations/TopicTagConfiguration.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Diwink.Extensions.EntityFrameworkCore.TestModel.csproj
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/Course.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/CourseMentorAssignment.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/CoursePolicy.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/LearningCatalog.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/Mentor.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/MentorWorkspace.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/TopicTag.cs
  • src/Diwink.Extensions.EntityFrameworkCore.TestModel/TestDbContext.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/ManyToMany/ManyToManySafetyContractTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/ManyToMany/PayloadAssociationContractTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/ManyToMany/PureManyToManyContractTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/OneToOne/OptionalOneToOneContractTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/OneToOne/RequiredOneToOneContractTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/Rejection/PartialMutationNotAllowedTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/Rejection/UnloadedNavigationMutationTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/Rejection/UnsupportedRelationshipPatternTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Diwink.Extensions.EntityFrameworkCore.Tests.Integration.csproj
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrap.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrapTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestCollection.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedData.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SeedDataStructureTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/SqlServerContainerFixture.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/Diwink.Extensions.EntityFrameworkCore.Tests.Unit.csproj
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/Exceptions/GraphUpdateExceptionTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/DbContextExtensionsTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/EntityKeyHelperTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/ExceptionValidationTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/NestedNavigationValidationTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/OperationGuardTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/RelationshipClassificationTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/GraphDiff/UnsupportedRelationshipPatternTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/RelationshipSemantics/ManyToManyDiffStrategyTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore.Tests.Unit/RelationshipSemantics/OneToOneOwnershipResolverTests.cs
  • src/Diwink.Extensions.EntityFrameworkCore/DbContextExtensions.cs
  • src/Diwink.Extensions.EntityFrameworkCore/Diwink.Extensions.EntityFrameworkCore.csproj
  • src/Diwink.Extensions.EntityFrameworkCore/Exceptions/AmbiguousOwnershipSemanticsException.cs
  • src/Diwink.Extensions.EntityFrameworkCore/Exceptions/GraphUpdateException.cs
  • src/Diwink.Extensions.EntityFrameworkCore/Exceptions/PartialMutationNotAllowedException.cs
  • src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnloadedNavigationMutationException.cs
  • src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnsupportedNavigationMutatedException.cs
  • src/Diwink.Extensions.EntityFrameworkCore/Exceptions/UnsupportedRelationshipPatternException.cs
  • src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/EntityKeyHelper.cs
  • src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/GraphUpdateOrchestrator.cs
  • src/Diwink.Extensions.EntityFrameworkCore/GraphUpdate/OperationGuard.cs
  • src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OneToOneOwnershipResolver.cs
  • src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OptionalOneToOneStrategy.cs
  • src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs
  • src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs
  • src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/RequiredOneToOneStrategy.cs
  • src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs
  • src/EFCore.UpdateGraph.slnx
  • src/FakeModel/FakeModel.csproj
  • src/FakeModel/Model.cs
  • src/UnitTests/FakeSchoolsDbContext.cs
  • src/UnitTests/FakeSchoolsDbContextFactory.cs
  • src/UnitTests/GraphUpdateTests.cs
  • src/UnitTests/Migrations/20200914083038_initialMigration.Designer.cs
  • src/UnitTests/Migrations/20200914083038_initialMigration.cs
  • src/UnitTests/Migrations/FakeSchoolsDbContextModelSnapshot.cs
  • src/UnitTests/TestHelpers.cs
  • src/UnitTests/UnitTests.csproj
  • src/UnitTests/VariousTests.cs
  • src/UnitTests/appsettings.json
💤 Files with no reviewable changes (13)
  • src/UnitTests/appsettings.json
  • src/FakeModel/FakeModel.csproj
  • EfCore.UpdateGraph.sln
  • src/UnitTests/TestHelpers.cs
  • src/UnitTests/UnitTests.csproj
  • src/UnitTests/Migrations/FakeSchoolsDbContextModelSnapshot.cs
  • src/UnitTests/Migrations/20200914083038_initialMigration.cs
  • src/UnitTests/Migrations/20200914083038_initialMigration.Designer.cs
  • src/UnitTests/VariousTests.cs
  • src/UnitTests/FakeSchoolsDbContext.cs
  • src/FakeModel/Model.cs
  • src/UnitTests/GraphUpdateTests.cs
  • src/UnitTests/FakeSchoolsDbContextFactory.cs

Comment on lines +12 to +13
// Required one-to-one
public CoursePolicy? Policy { get; set; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Comment-code inconsistency: "Required" vs nullable type.

The comment states "Required one-to-one" but Policy is declared as nullable (CoursePolicy?). If the relationship is truly required at the database level, consider either:

  1. Making the property non-nullable (CoursePolicy Policy) with null! initialization, or
  2. Updating the comment to clarify it's optional from a C# perspective but enforced by EF Core configuration.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Diwink.Extensions.EntityFrameworkCore.TestModel/Entities/Course.cs`
around lines 12 - 13, The comment and code disagree: the Course class's Policy
property is marked as nullable (CoursePolicy? Policy) while the comment says
"Required one-to-one"; decide which is correct and make them consistent. If the
relationship is required, change the property to non-nullable (CoursePolicy
Policy) and initialize with null! or ensure constructor assignment so the
compiler is satisfied; if it is optional, update the comment to indicate it's
optional/nullable and ensure EF Core configuration (in the Course/CoursePolicy
mapping) matches. Reference: Course.Policy and the CoursePolicy entity.

Comment on lines +82 to +85
// Assert
await using var verifyCtx = CreateContext();
var mentorCount = await verifyCtx.Mentors.CountAsync();
mentorCount.Should().Be(2, "removing all payload associations must not delete any mentor entities");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Also assert that assignment links were actually removed.

This test currently verifies mentor entities are preserved, but it does not assert that Course.MentorAssignments became empty. It can pass even if unlinking fails.

💡 Proposed test hardening
         // Assert
         await using var verifyCtx = CreateContext();
+        var updatedCourse = await verifyCtx.Courses
+            .Include(c => c.MentorAssignments)
+            .FirstAsync(c => c.Id == SeedData.Course1Id);
+        updatedCourse.MentorAssignments.Should().BeEmpty();
+
         var mentorCount = await verifyCtx.Mentors.CountAsync();
         mentorCount.Should().Be(2, "removing all payload associations must not delete any mentor entities");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Assert
await using var verifyCtx = CreateContext();
var mentorCount = await verifyCtx.Mentors.CountAsync();
mentorCount.Should().Be(2, "removing all payload associations must not delete any mentor entities");
// Assert
await using var verifyCtx = CreateContext();
var updatedCourse = await verifyCtx.Courses
.Include(c => c.MentorAssignments)
.FirstAsync(c => c.Id == SeedData.Course1Id);
updatedCourse.MentorAssignments.Should().BeEmpty();
var mentorCount = await verifyCtx.Mentors.CountAsync();
mentorCount.Should().Be(2, "removing all payload associations must not delete any mentor entities");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/ManyToMany/ManyToManySafetyContractTests.cs`
around lines 82 - 85, The test ManyToManySafetyContractTests should also assert
that assignment links were removed: after creating verifyCtx and verifying
mentorCount, load the affected Course(s) including the MentorAssignments
navigation (e.g., via verifyCtx.Courses.Include(c => c.MentorAssignments) or
fetching the specific course by id) and assert that course.MentorAssignments is
empty (Count==0 or .Should().BeEmpty()), ensuring the unlink operation actually
cleared Course.MentorAssignments in addition to preserving Mentor entities.

Comment on lines +14 to +17
public class PartialMutationNotAllowedTests : IntegrationTestBase
{
public PartialMutationNotAllowedTests(SqlServerContainerFixture fixture)
: base(fixture) { }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if IntegrationTestBase has the Collection attribute or if there's assembly-level configuration

# Check IntegrationTestBase for Collection attribute
echo "=== Checking IntegrationTestBase for Collection attribute ==="
rg -n -A5 "class IntegrationTestBase" --type cs

# Check for assembly-level collection configuration
echo "=== Checking for assembly-level attributes ==="
rg -n "CollectionBehavior|Collection\(" --type cs -g "*Assembly*" -g "*Global*"

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 1150


🏁 Script executed:

#!/bin/bash
# Check complete IntegrationTestBase class definition (attributes and full class declaration)
echo "=== Full IntegrationTestBase definition ==="
rg -B10 "public abstract class IntegrationTestBase" --type cs

# Search for Collection attribute anywhere in the integration tests
echo "=== Searching for Collection attribute in integration tests ==="
rg -n "\[Collection" src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/ --type cs

# Check if AssemblyInfo or module initializer exists
echo "=== Looking for assembly-level configuration ==="
fd -e "AssemblyInfo\|cs\|GlobalUsings\|cs" src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/ | head -20

# Look at the specific test file being reviewed
echo "=== Check PartialMutationNotAllowedTests.cs ==="
cat -n src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/Rejection/PartialMutationNotAllowedTests.cs | head -30

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 4601


Add explicit [Collection(IntegrationTestCollection.Name)] attribute to the test class.

xUnit does not inherit attributes from base classes. While IntegrationTestBase has the [Collection(IntegrationTestCollection.Name)] attribute, derived test classes must also have it explicitly to be placed in the collection. Other test classes in this project (e.g., PureManyToManyContractTests, PayloadAssociationContractTests) declare this attribute directly; PartialMutationNotAllowedTests should match this pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Contracts/Rejection/PartialMutationNotAllowedTests.cs`
around lines 14 - 17, Add the missing xUnit collection attribute to the test
class: add [Collection(IntegrationTestCollection.Name)] directly above the
PartialMutationNotAllowedTests class declaration so the class
(PartialMutationNotAllowedTests) is explicitly placed in the
IntegrationTestCollection (IntegrationTestCollection.Name) rather than relying
on inheritance from IntegrationTestBase.

Comment on lines +18 to +24
/// <summary>
/// Creates a fresh DbContext pointing at the container and ensures the schema exists.
/// <summary>
/// Creates a TestDbContext configured to use SQL Server with a test-specific connection string.
/// </summary>
/// <param name="connectionString">Base SQL Server connection string; if its initial catalog is empty or equals "master", the catalog will be replaced with the test database name.</param>
/// <returns>A new TestDbContext configured with the adjusted connection string.</returns>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the malformed XML doc blocks.

These method comments have the same nested <summary> issue as the strategy file, so the generated XML docs will be invalid here too.

Also applies to: 53-59, 67-73

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/DatabaseBootstrap.cs`
around lines 18 - 24, Fix the malformed XML doc comments in DatabaseBootstrap:
remove the nested/duplicate <summary> tags and ensure each summary has one
opening <summary> and one closing </summary> around the intended text (for the
method that "Creates a fresh DbContext..." / the method that "Creates a
TestDbContext configured to use SQL Server..." and the other doc blocks
mentioned). Update the related XML comments (the other blocks at the same spots)
to use a single well-formed <summary> per comment and correct any mismatched
closing tags so the generated XML docs are valid.

Comment on lines +41 to +45
/// <summary>
/// Creates a fresh DbContext for the current test.
/// <summary>
/// Creates a new TestDbContext configured for the integration test database.
/// </summary>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs | sed -n '35,50p'

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 865


Remove the duplicate <summary> tag on line 43.

The XML documentation contains two opening <summary> tags (lines 41 and 43) with only one closing </summary> tag, which causes malformed documentation. Remove the second opening tag on line 43 to keep a single valid summary block.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore.Tests.Integration/Infrastructure/IntegrationTestBase.cs`
around lines 41 - 45, Remove the duplicate XML documentation opening tag in the
summary block for the method in IntegrationTestBase: delete the second
"<summary>" line (the one starting "Creates a new TestDbContext...") so there is
only one opening <summary> and a single closing </summary> for that method's
documentation in IntegrationTestBase.

Comment on lines +14 to +19
/// <summary>
/// Detaches the optional dependent by nulling its FK properties.
/// The dependent entity is preserved in the database with a null FK.
/// <summary>
/// Clears the given optional dependent navigation on the tracked principal so the dependent's foreign key is set to null while leaving the dependent entity intact.
/// </summary>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the malformed XML doc blocks.

Each method opens a second <summary> before closing the first one. That leaves the XML docs invalid and can trigger compiler documentation warnings when XML output is enabled.

Also applies to: 36-41, 59-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/OptionalOneToOneStrategy.cs`
around lines 14 - 19, The XML documentation in OptionalOneToOneStrategy has
duplicate opening <summary> tags causing malformed docs; edit each affected doc
block (the summaries describing detaching/clearing the optional dependent
navigation in the OptionalOneToOneStrategy class) to have a single opening
<summary> and a single closing </summary> wrapping the combined description,
removing the extra <summary> tags and ensuring proper XML structure for all
similar blocks (the other two summary blocks in the same class) so the doc
comments are valid.

Comment on lines +47 to +61
foreach (var updatedItem in updatedItems)
{
var updatedKeys = EntityKeyHelper.GetKeyValues(context, updatedItem);
var existingMatch = FindInTracked(context, existingItems, updatedKeys);

if (existingMatch is not null)
{
// Update payload fields on existing association entity
context.Entry(existingMatch).CurrentValues.SetValues(updatedItem);
}
else
{
// New association entity — add to collection
AddToCollection(existingNavigation, updatedItem);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard against duplicate keys in updatedCollection to avoid double-add/PK conflicts.

If updatedCollection contains two association entities with the same key and no tracked match exists, both will be added. This can cause duplicate relationship rows or SaveChanges key violations.

💡 Proposed fix
+        var processedKeys = new List<object[]>();
+
         // Add new or update existing association entities
         foreach (var updatedItem in updatedItems)
         {
             var updatedKeys = EntityKeyHelper.GetKeyValues(context, updatedItem);
+            if (processedKeys.Any(k => EntityKeyHelper.KeysEqual(k, updatedKeys)))
+            {
+                throw new InvalidOperationException(
+                    $"Duplicate association entity key detected for navigation " +
+                    $"'{existingNavigation.Metadata.DeclaringEntityType.ClrType.Name}.{existingNavigation.Metadata.Name}'.");
+            }
+            processedKeys.Add(updatedKeys);
+
             var existingMatch = FindInTracked(context, existingItems, updatedKeys);
 
             if (existingMatch is not null)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PayloadManyToManyStrategy.cs`
around lines 47 - 61, updatedItems can contain duplicate association keys which,
if no tracked match exists, will cause multiple AddToCollection calls and PK
conflicts; inside the loop over updatedItems (where
EntityKeyHelper.GetKeyValues, FindInTracked and AddToCollection are used) add a
guard: compute a stable signature for updatedKeys (e.g., join key values or use
a structural comparer) and keep a HashSet of seen signatures; if a signature is
already seen, skip or merge payloads into the already-processed association
instead of calling AddToCollection again; ensure this check runs before calling
FindInTracked/AddToCollection so duplicates are handled deterministically.

Comment on lines +86 to +93
private static void ApplyValuesIfNotModified(DbContext context, object trackedEntity, object updatedEntity)
{
var trackedEntry = context.Entry(trackedEntity);
if (trackedEntry.State is EntityState.Unchanged or EntityState.Detached)
{
trackedEntry.CurrentValues.SetValues(updatedEntity);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how ApplyValuesIfNotModified is called and whether detached entities reach this code path
ast-grep --pattern $'ApplyValuesIfNotModified($_, $_, $_)'

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 433


🏁 Script executed:

cat -n src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs | sed -n '45,85p'

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 2049


🏁 Script executed:

cat -n src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs | sed -n '20,50p'

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 1950


🏁 Script executed:

cat -n src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs | sed -n '102,120p'

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 938


🏁 Script executed:

cat -n src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs | sed -n '86,95p'

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 517


🌐 Web query:

EF Core context.Entry() behavior on detached entities attach

💡 Result:

In EF Core, calling context.Entry on a detached entity returns an EntityEntry instance without starting to track the entity. The entity's state remains Detached. You can then use the returned EntityEntry to change the state (e.g., to Added, Modified, Unchanged), at which point the entity becomes tracked in that state. This differs from EF6, where Entry might behave differently. Unlike Attach or Add, which affect the entire entity graph, Entry primarily affects only the specified root entity and ignores child entities unless explicitly handled. Example from official docs: var newBlog = new Blog; Debug.Assert(context.Entry(newBlog).State == EntityState.Detached); context.Entry(newBlog).State = EntityState.Added; Debug.Assert(context.Entry(newBlog).State == EntityState.Added); This does not automatically attach related entities; it is a lower-level operation focused on the single entity instance provided.

Citations:


🌐 Web query:

EF Core SetValues CurrentValues detached entity behavior

💡 Result:

In EF Core, calling CurrentValues.SetValues on a detached entity has no effect and does not track the entity or mark properties as modified. The CurrentValues property and its SetValues method are only available and functional on tracked entities (EntityState other than Detached). To update a detached entity: 1. Query the existing tracked entity from the database using the same DbContext: var existing = await context.Entities.FindAsync(id); 2. If found, call context.Entry(existing).CurrentValues.SetValues(detachedEntity); This copies scalar property values from the detached/source entity to the tracked entity's CurrentValues, marks changed properties as Modified, and ensures only changed properties are updated on SaveChanges. 3. Call SaveChanges to persist. Key behaviors and limitations: - Works on primitive/scalar properties of the principal entity. - Does not update navigation properties, collections, or owned types automatically. For owned types, explicitly access the reference: context.Entry(existing).Reference("OwnedNav").TargetEntry.CurrentValues.SetValues(sourceOwned); (sources 1,18). - If the entity doesn't exist, use context.Add(detachedEntity) instead. - For full disconnected scenarios (e.g., web APIs), this pattern is recommended in official docs (source 11). Example: var detached = new Entity { Id = 1, Name = "Updated" }; // from client var existing = await context.Entities.FindAsync(1); if (existing != null) { context.Entry(existing).CurrentValues.SetValues(detached); await context.SaveChangesAsync; } Using context.Update(detached) marks all properties as Modified (less efficient) and tracks the detached instance directly.

Citations:


Remove EntityState.Detached from the condition—SetValues on a detached entity will have no effect.

When trackedEntry.State is Detached, the entity is not being tracked by the context. Calling CurrentValues.SetValues(updatedEntity) on a detached entry will not update values or mark properties as modified since the operation only works on tracked entities. While detached entities are unlikely to reach this code path in the current flow (both call sites should have tracked entities), including this condition is semantically incorrect and could silently fail if the method is called with a detached entity in the future.

Proposed fix
     private static void ApplyValuesIfNotModified(DbContext context, object trackedEntity, object updatedEntity)
     {
         var trackedEntry = context.Entry(trackedEntity);
-        if (trackedEntry.State is EntityState.Unchanged or EntityState.Detached)
+        if (trackedEntry.State is EntityState.Unchanged)
         {
             trackedEntry.CurrentValues.SetValues(updatedEntity);
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static void ApplyValuesIfNotModified(DbContext context, object trackedEntity, object updatedEntity)
{
var trackedEntry = context.Entry(trackedEntity);
if (trackedEntry.State is EntityState.Unchanged or EntityState.Detached)
{
trackedEntry.CurrentValues.SetValues(updatedEntity);
}
}
private static void ApplyValuesIfNotModified(DbContext context, object trackedEntity, object updatedEntity)
{
var trackedEntry = context.Entry(trackedEntity);
if (trackedEntry.State is EntityState.Unchanged)
{
trackedEntry.CurrentValues.SetValues(updatedEntity);
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/PureManyToManyStrategy.cs`
around lines 86 - 93, The condition in ApplyValuesIfNotModified incorrectly
includes EntityState.Detached; calling
trackedEntry.CurrentValues.SetValues(updatedEntity) on a detached entry is a
no-op. Update the method ApplyValuesIfNotModified to only call SetValues when
the tracked entry is actually tracked (e.g., when trackedEntry.State is
EntityState.Unchanged) by removing the "EntityState.Detached" branch from the if
condition so SetValues is only invoked for tracked entities (use the
trackedEntry variable and Entry/CurrentValues as in the existing method).

Comment on lines +13 to +20
/// <summary>
/// Removes the required dependent by marking it for deletion.
/// EF Core will delete the row on SaveChanges.
/// <summary>
/// Marks the dependent entity referenced by <paramref name="existingNavigation"/> for deletion if it is currently tracked.
/// </summary>
/// <param name="context">The <see cref="DbContext"/> used to mark the dependent entity for removal.</param>
/// <param name="existingNavigation">The tracked reference entry whose <c>CurrentValue</c> is the dependent entity to remove; no action is taken if its value is <c>null</c>.</param>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify XML-doc tag pairing in this file
rg -n '///\s*</?summary>|///\s*</?param>|///\s*</?remarks>|///\s*</?returns>' src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/RequiredOneToOneStrategy.cs

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 179


Fix malformed XML documentation tags on RemoveDependent.

Line 16 has a second <summary> opening tag instead of closing the first one. This breaks XML documentation validity and may cause build failures.

Proposed fix
    /// <summary>
    /// Removes the required dependent by marking it for deletion.
    /// EF Core will delete the row on SaveChanges.
-   /// <summary>
+   /// </summary>
    /// Marks the dependent entity referenced by <paramref name="existingNavigation"/> for deletion if it is currently tracked.
-   /// </summary>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <summary>
/// Removes the required dependent by marking it for deletion.
/// EF Core will delete the row on SaveChanges.
/// <summary>
/// Marks the dependent entity referenced by <paramref name="existingNavigation"/> for deletion if it is currently tracked.
/// </summary>
/// <param name="context">The <see cref="DbContext"/> used to mark the dependent entity for removal.</param>
/// <param name="existingNavigation">The tracked reference entry whose <c>CurrentValue</c> is the dependent entity to remove; no action is taken if its value is <c>null</c>.</param>
/// <summary>
/// Removes the required dependent by marking it for deletion.
/// EF Core will delete the row on SaveChanges.
/// </summary>
/// Marks the dependent entity referenced by <paramref name="existingNavigation"/> for deletion if it is currently tracked.
/// </summary>
/// <param name="context">The <see cref="DbContext"/> used to mark the dependent entity for removal.</param>
/// <param name="existingNavigation">The tracked reference entry whose <c>CurrentValue</c> is the dependent entity to remove; no action is taken if its value is <c>null</c>.</param>
Suggested change
/// <summary>
/// Removes the required dependent by marking it for deletion.
/// EF Core will delete the row on SaveChanges.
/// <summary>
/// Marks the dependent entity referenced by <paramref name="existingNavigation"/> for deletion if it is currently tracked.
/// </summary>
/// <param name="context">The <see cref="DbContext"/> used to mark the dependent entity for removal.</param>
/// <param name="existingNavigation">The tracked reference entry whose <c>CurrentValue</c> is the dependent entity to remove; no action is taken if its value is <c>null</c>.</param>
/// <summary>
/// Removes the required dependent by marking it for deletion.
/// EF Core will delete the row on SaveChanges.
/// </summary>
/// Marks the dependent entity referenced by <paramref name="existingNavigation"/> for deletion if it is currently tracked.
/// <param name="context">The <see cref="DbContext"/> used to mark the dependent entity for removal.</param>
/// <param name="existingNavigation">The tracked reference entry whose <c>CurrentValue</c> is the dependent entity to remove; no action is taken if its value is <c>null</c>.</param>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Diwink.Extensions.EntityFrameworkCore/RelationshipStrategies/RequiredOneToOneStrategy.cs`
around lines 13 - 20, The XML doc for RemoveDependent in
RequiredOneToOneStrategy has a malformed tag: the second "<summary>" should be a
closing "</summary>" which breaks the documentation; update the block for the
RemoveDependent method by replacing that erroneous opening <summary> with
</summary> and ensure the remaining XML tags (parameters and summaries) are
properly closed so the doc comment is well-formed.

Comment on lines +16 to +20
/// Ensures all navigations in the existing entity entry that the caller expects
/// to participate in graph mutation are explicitly loaded.
/// <summary>
/// Validates loaded navigations on the tracked entity and its reachable object graph, ensuring required navigations are present before mutation.
/// </summary>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Detect duplicated <summary> openings before a matching </summary> in this file.
rg -nU '///\s*<summary>\n(?:(?!///\s*</summary>).*\n)*///\s*<summary>' src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 366


🏁 Script executed:

cat -n src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs

Repository: WahidBitar/EF-Core-Simple-Graph-Update

Length of output: 4550


Remove the duplicated <summary> opening tags in the XML documentation blocks.

Lines 15-20 and 64-69 each contain two <summary> opening tags but only one closing </summary> tag, causing malformed XML documentation. Delete the second opening <summary> tag (line 18 and line 67 respectively) to fix the structure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Diwink.Extensions.EntityFrameworkCore/Traversal/NavigationLoadGuard.cs`
around lines 16 - 20, In the XML docs for the NavigationLoadGuard class, remove
the duplicated opening <summary> tag occurrences so each documentation block has
a single <summary> ... </summary> pair; specifically delete the extra <summary>
in the class/header doc and the extra <summary> in the other documentation block
within NavigationLoadGuard.cs so the XML is well-formed around the
NavigationLoadGuard documentation.

@WahidBitar WahidBitar changed the title @CodeRabbit Rebuild graph update system with contract-driven relationship semantics v2 rebuild: EF Core 10 targeting with modular refactoring @CodeRabbit Apr 5, 2026
@WahidBitar WahidBitar changed the title Rebuild graph update system with contract-driven relationship semantics v2 rebuild: EF Core 10 targeting with modular refactoring @CodeRabbit Rebuild graph update system with contract-driven relationship semantics v2 rebuild: EF Core 10 targeting with modular refactoring @coderabbitai Apr 5, 2026
@WahidBitar WahidBitar merged commit 5102852 into master Apr 5, 2026
3 checks passed
@WahidBitar WahidBitar deleted the 001-rebuild-graph-update branch April 5, 2026 10:58
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.

1 participant