@@ -434,4 +434,158 @@ await _context.ExecuteBulkInsertAsync(new[] { blog }, options =>
434434 insertedTags . Should ( ) . HaveCount ( 2 ) ;
435435 insertedTags . Should ( ) . AllSatisfy ( t => t . Id . Should ( ) . BeGreaterThan ( 0 ) ) ;
436436 }
437+
438+ [ SkippableFact ]
439+ public async Task InsertGraph_OriginalEntitiesLinked_WithGeneratedKeys ( )
440+ {
441+ // Arrange - Keep references to original entities
442+ var post1 = new Post { TestRun = _run , Title = $ "{ _run } _LinkedPost1" } ;
443+ var post2 = new Post { TestRun = _run , Title = $ "{ _run } _LinkedPost2" } ;
444+ var settings = new BlogSettings { TestRun = _run , EnableComments = true } ;
445+ var blog = new Blog
446+ {
447+ TestRun = _run ,
448+ Name = $ "{ _run } _LinkedBlog",
449+ Posts = new List < Post > { post1 , post2 } ,
450+ Settings = settings
451+ } ;
452+
453+ // Act
454+ await _context . ExecuteBulkInsertAsync ( new [ ] { blog } , options =>
455+ {
456+ options . IncludeGraph = true ;
457+ } ) ;
458+
459+ // Assert - Verify the original entity references are the same objects
460+ // and have their generated IDs populated
461+ blog . Id . Should ( ) . BeGreaterThan ( 0 , "Blog should have generated ID populated" ) ;
462+ post1 . Id . Should ( ) . BeGreaterThan ( 0 , "Post1 should have generated ID populated" ) ;
463+ post2 . Id . Should ( ) . BeGreaterThan ( 0 , "Post2 should have generated ID populated" ) ;
464+ settings . Id . Should ( ) . BeGreaterThan ( 0 , "Settings should have generated ID populated" ) ;
465+
466+ // Verify FK values are propagated
467+ post1 . BlogId . Should ( ) . Be ( blog . Id , "Post1.BlogId should reference the Blog" ) ;
468+ post2 . BlogId . Should ( ) . Be ( blog . Id , "Post2.BlogId should reference the Blog" ) ;
469+ settings . BlogId . Should ( ) . Be ( blog . Id , "Settings.BlogId should reference the Blog" ) ;
470+
471+ // Verify the same objects are in the collections
472+ blog . Posts . Should ( ) . Contain ( post1 , "Original post1 reference should still be in the collection" ) ;
473+ blog . Posts . Should ( ) . Contain ( post2 , "Original post2 reference should still be in the collection" ) ;
474+ blog . Settings . Should ( ) . BeSameAs ( settings , "Original settings reference should still be assigned" ) ;
475+
476+ // Verify data matches what's in the database
477+ var dbBlog = _context . Blogs . FirstOrDefault ( b => b . Id == blog . Id ) ;
478+ dbBlog . Should ( ) . NotBeNull ( ) ;
479+ dbBlog ! . Name . Should ( ) . Be ( blog . Name ) ;
480+
481+ var dbPosts = _context . Posts . Where ( p => p . BlogId == blog . Id ) . ToList ( ) ;
482+ dbPosts . Should ( ) . HaveCount ( 2 ) ;
483+ dbPosts . Select ( p => p . Id ) . Should ( ) . Contain ( post1 . Id ) ;
484+ dbPosts . Select ( p => p . Id ) . Should ( ) . Contain ( post2 . Id ) ;
485+ }
486+
487+ [ SkippableFact ]
488+ public async Task InsertGraph_OriginalEntitiesLinked_WithClientGeneratedKeys ( )
489+ {
490+ // Arrange - Create entities with pre-set GUIDs (client-generated keys)
491+ // Using TestEntityWithGuidId which has ValueGeneratedNever()
492+ var guid1 = Guid . NewGuid ( ) ;
493+ var guid2 = Guid . NewGuid ( ) ;
494+
495+ var entity1 = new TestEntityWithGuidId
496+ {
497+ TestRun = _run ,
498+ Id = guid1 ,
499+ Name = $ "{ _run } _ClientGenKey1"
500+ } ;
501+ var entity2 = new TestEntityWithGuidId
502+ {
503+ TestRun = _run ,
504+ Id = guid2 ,
505+ Name = $ "{ _run } _ClientGenKey2"
506+ } ;
507+
508+ // Act - Insert without graph (since these don't have navigations)
509+ // but test that client-generated keys are preserved
510+ await _context . ExecuteBulkInsertAsync ( new [ ] { entity1 , entity2 } , options =>
511+ {
512+ // No IncludeGraph needed since no navigations
513+ } ) ;
514+
515+ // Assert - Verify the original entity references maintain their IDs
516+ entity1 . Id . Should ( ) . Be ( guid1 , "Entity1 should retain its client-generated ID" ) ;
517+ entity2 . Id . Should ( ) . Be ( guid2 , "Entity2 should retain its client-generated ID" ) ;
518+
519+ // Verify data is in database with the same IDs
520+ var dbEntity1 = _context . TestEntitiesWithGuidId . FirstOrDefault ( e => e . Id == guid1 ) ;
521+ var dbEntity2 = _context . TestEntitiesWithGuidId . FirstOrDefault ( e => e . Id == guid2 ) ;
522+
523+ dbEntity1 . Should ( ) . NotBeNull ( ) ;
524+ dbEntity2 . Should ( ) . NotBeNull ( ) ;
525+ dbEntity1 ! . Name . Should ( ) . Be ( entity1 . Name ) ;
526+ dbEntity2 ! . Name . Should ( ) . Be ( entity2 . Name ) ;
527+ }
528+
529+ [ SkippableFact ]
530+ public async Task InsertGraph_MultipleRootEntities_OriginalEntitiesLinked ( )
531+ {
532+ // Arrange - Multiple root entities with children, keep all references
533+ var post1 = new Post { TestRun = _run , Title = $ "{ _run } _Multi1Post1" } ;
534+ var post2 = new Post { TestRun = _run , Title = $ "{ _run } _Multi2Post1" } ;
535+ var post3 = new Post { TestRun = _run , Title = $ "{ _run } _Multi2Post2" } ;
536+
537+ var blog1 = new Blog
538+ {
539+ TestRun = _run ,
540+ Name = $ "{ _run } _MultiBlogLinked1",
541+ Posts = new List < Post > { post1 }
542+ } ;
543+ var blog2 = new Blog
544+ {
545+ TestRun = _run ,
546+ Name = $ "{ _run } _MultiBlogLinked2",
547+ Posts = new List < Post > { post2 , post3 }
548+ } ;
549+
550+ var blogs = new [ ] { blog1 , blog2 } ;
551+
552+ // Act
553+ await _context . ExecuteBulkInsertAsync ( blogs , options =>
554+ {
555+ options . IncludeGraph = true ;
556+ } ) ;
557+
558+ // Assert - All original entities should have IDs and be linked correctly
559+ blog1 . Id . Should ( ) . BeGreaterThan ( 0 ) ;
560+ blog2 . Id . Should ( ) . BeGreaterThan ( 0 ) ;
561+ blog1 . Id . Should ( ) . NotBe ( blog2 . Id , "Different blogs should have different IDs" ) ;
562+
563+ post1 . Id . Should ( ) . BeGreaterThan ( 0 ) ;
564+ post2 . Id . Should ( ) . BeGreaterThan ( 0 ) ;
565+ post3 . Id . Should ( ) . BeGreaterThan ( 0 ) ;
566+ post1 . Id . Should ( ) . NotBe ( post2 . Id ) ;
567+ post2 . Id . Should ( ) . NotBe ( post3 . Id ) ;
568+
569+ // Verify FK relationships
570+ post1 . BlogId . Should ( ) . Be ( blog1 . Id ) ;
571+ post2 . BlogId . Should ( ) . Be ( blog2 . Id ) ;
572+ post3 . BlogId . Should ( ) . Be ( blog2 . Id ) ;
573+
574+ // Verify original objects are still in collections
575+ blog1 . Posts . Should ( ) . Contain ( post1 ) ;
576+ blog2 . Posts . Should ( ) . Contain ( post2 ) ;
577+ blog2 . Posts . Should ( ) . Contain ( post3 ) ;
578+
579+ // Verify database state matches
580+ var dbBlogs = _context . Blogs . Where ( b => b . TestRun == _run ) . ToList ( ) ;
581+ dbBlogs . Should ( ) . HaveCount ( 2 ) ;
582+ dbBlogs . Select ( b => b . Id ) . Should ( ) . Contain ( blog1 . Id ) ;
583+ dbBlogs . Select ( b => b . Id ) . Should ( ) . Contain ( blog2 . Id ) ;
584+
585+ var dbPosts = _context . Posts . Where ( p => p . TestRun == _run ) . ToList ( ) ;
586+ dbPosts . Should ( ) . HaveCount ( 3 ) ;
587+ dbPosts . Select ( p => p . Id ) . Should ( ) . Contain ( post1 . Id ) ;
588+ dbPosts . Select ( p => p . Id ) . Should ( ) . Contain ( post2 . Id ) ;
589+ dbPosts . Select ( p => p . Id ) . Should ( ) . Contain ( post3 . Id ) ;
590+ }
437591}
0 commit comments