diff --git a/src/EFCore/ChangeTracking/EntityEntryGraphNode`.cs b/src/EFCore/ChangeTracking/EntityEntryGraphNode`.cs
index de64cce50d8..5c7ca6480e7 100644
--- a/src/EFCore/ChangeTracking/EntityEntryGraphNode`.cs
+++ b/src/EFCore/ChangeTracking/EntityEntryGraphNode`.cs
@@ -37,7 +37,7 @@ public EntityEntryGraphNode(
///
/// Gets or sets state that will be available to all nodes that are visited after this node.
///
- public virtual TState NodeState { get; }
+ public virtual TState NodeState { get; [param: CanBeNull] set; }
///
/// Creates a new node for the entity that is being traversed next in the graph.
diff --git a/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs b/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs
index 958dd3d167b..9be8eb6c891 100644
--- a/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs
+++ b/test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs
@@ -6,7 +6,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -1098,7 +1097,7 @@ private static readonly IServiceProvider _poolProvider
= new ServiceCollection()
.AddDbContextPool(
p => p.UseInMemoryDatabase(nameof(LikeAZooContextPooled))
- .UseInternalServiceProvider(InMemoryFixture.BuildServiceProvider(_loggerFactory)))
+ .UseInternalServiceProvider(InMemoryFixture.BuildServiceProvider(_loggerFactory)))
.BuildServiceProvider();
private class LikeAZooContextPooled : LikeAZooContext
@@ -1301,841 +1300,6 @@ public void Can_get_Context()
Assert.Same(context, context.ChangeTracker.Context);
}
- private static string NodeString(EntityEntryGraphNode node)
- => EntryString(node.SourceEntry)
- + " ---"
- + node.InboundNavigation?.Name
- + "--> "
- + EntryString(node.Entry);
-
- private static string EntryString(EntityEntry entry)
- => entry == null
- ? ""
- : entry.Metadata.DisplayName()
- + ":"
- + entry.Property(entry.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue;
-
- [ConditionalTheory]
- [InlineData(false, false)]
- [InlineData(false, true)]
- [InlineData(true, false)]
- [InlineData(true, true)]
- public void Can_attach_nullable_PK_parent_with_child_collection(bool useAttach, bool setKeys)
- {
- using var context = new EarlyLearningCenter();
- var category = new NullbileCategory
- {
- Products = new List
- {
- new NullbileProduct(),
- new NullbileProduct(),
- new NullbileProduct()
- }
- };
-
- if (setKeys)
- {
- context.Entry(category).Property("Id").CurrentValue = 1;
- context.Entry(category.Products[0]).Property("Id").CurrentValue = 1;
- context.Entry(category.Products[1]).Property("Id").CurrentValue = 2;
- context.Entry(category.Products[2]).Property("Id").CurrentValue = 3;
- }
-
- if (useAttach)
- {
- context.Attach(category);
- }
- else
- {
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- category, e =>
- {
- e.Entry.State = e.Entry.IsKeySet ? EntityState.Unchanged : EntityState.Added;
- traversal.Add(NodeString(e));
- });
-
- Assert.Equal(
- new List
- {
- " -----> NullbileCategory:1",
- "NullbileCategory:1 ---Products--> NullbileProduct:1",
- "NullbileCategory:1 ---Products--> NullbileProduct:2",
- "NullbileCategory:1 ---Products--> NullbileProduct:3"
- },
- traversal);
- }
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- var categoryEntry = context.Entry(category);
- var product0Entry = context.Entry(category.Products[0]);
- var product1Entry = context.Entry(category.Products[1]);
- var product2Entry = context.Entry(category.Products[2]);
-
- var expectedState = setKeys ? EntityState.Unchanged : EntityState.Added;
- Assert.Equal(expectedState, categoryEntry.State);
- Assert.Equal(expectedState, product0Entry.State);
- Assert.Equal(expectedState, product1Entry.State);
- Assert.Equal(expectedState, product2Entry.State);
-
- Assert.Same(category, category.Products[0].Category);
- Assert.Same(category, category.Products[1].Category);
- Assert.Same(category, category.Products[2].Category);
-
- var categoryId = categoryEntry.Property("Id").CurrentValue;
- Assert.NotNull(categoryId);
-
- Assert.Equal(categoryId, product0Entry.Property("CategoryId").CurrentValue);
- Assert.Equal(categoryId, product1Entry.Property("CategoryId").CurrentValue);
- Assert.Equal(categoryId, product2Entry.Property("CategoryId").CurrentValue);
- }
-
- [ConditionalTheory]
- [InlineData(false, false)]
- [InlineData(false, true)]
- [InlineData(true, false)]
- [InlineData(true, true)]
- public void Can_attach_nullable_PK_parent_with_one_to_one_children(bool useAttach, bool setKeys)
- {
- using var context = new EarlyLearningCenter();
- var category = new NullbileCategory { Info = new NullbileCategoryInfo() };
-
- if (setKeys)
- {
- context.Entry(category).Property("Id").CurrentValue = 1;
- context.Entry(category.Info).Property("Id").CurrentValue = 1;
- }
-
- if (useAttach)
- {
- context.Attach(category);
- }
- else
- {
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- category, e =>
- {
- e.Entry.State = e.Entry.IsKeySet ? EntityState.Unchanged : EntityState.Added;
- traversal.Add(NodeString(e));
- });
-
- Assert.Equal(
- new List { " -----> NullbileCategory:1", "NullbileCategory:1 ---Info--> NullbileCategoryInfo:1" },
- traversal);
- }
-
- Assert.Equal(2, context.ChangeTracker.Entries().Count());
-
- var expectedState = setKeys ? EntityState.Unchanged : EntityState.Added;
- Assert.Equal(expectedState, context.Entry(category).State);
- Assert.Equal(expectedState, context.Entry(category.Info).State);
-
- Assert.Same(category, category.Info.Category);
- }
-
- [ConditionalTheory]
- [InlineData(false, false, false)]
- [InlineData(false, true, false)]
- [InlineData(true, false, false)]
- [InlineData(true, true, false)]
- [InlineData(false, false, true)]
- [InlineData(false, true, true)]
- [InlineData(true, false, true)]
- [InlineData(true, true, true)]
- public void Can_attach_parent_with_owned_dependent(bool useAttach, bool setPrincipalKey, bool setDependentKey)
- {
- using var context = new EarlyLearningCenter();
- var sweet = new Sweet { Dreams = new Dreams { Are = new AreMade(), Made = new AreMade() } };
-
- if (setPrincipalKey)
- {
- sweet.Id = 1;
- }
-
- if (setDependentKey)
- {
- var dreamsEntry = context.Entry(sweet).Reference(e => e.Dreams).TargetEntry;
- dreamsEntry.Property("SweetId").CurrentValue = 1;
- dreamsEntry.Reference(e => e.Are).TargetEntry.Property("DreamsSweetId").CurrentValue = 1;
- dreamsEntry.Reference(e => e.Made).TargetEntry.Property("DreamsSweetId").CurrentValue = 1;
- }
-
- if (useAttach)
- {
- context.Attach(sweet);
- }
- else
- {
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- sweet, e =>
- {
- if (e.Entry.Metadata.IsOwned())
- {
- e.Entry.State = e.SourceEntry.State;
- }
- else
- {
- e.Entry.State = e.Entry.IsKeySet ? EntityState.Unchanged : EntityState.Added;
- }
-
- traversal.Add(NodeString(e));
- });
-
- Assert.Equal(
- new List
- {
- " -----> Sweet:1",
- "Sweet:1 ---Dreams--> Dreams:1",
- "Dreams:1 ---Are--> Dreams.Are#AreMade:1",
- "Dreams:1 ---Made--> Dreams.Made#AreMade:1"
- },
- traversal);
- }
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- var dependentEntry = context.Entry(sweet.Dreams);
- var dependentEntry2a = context.Entry(sweet.Dreams.Are);
- var dependentEntry2b = context.Entry(sweet.Dreams.Made);
-
- var expectedPrincipalState = setPrincipalKey ? EntityState.Unchanged : EntityState.Added;
- var expectedDependentState = setPrincipalKey || (setDependentKey && useAttach) ? EntityState.Unchanged : EntityState.Added;
-
- Assert.Equal(expectedPrincipalState, context.Entry(sweet).State);
- Assert.Equal(expectedDependentState, dependentEntry.State);
- Assert.Equal(expectedDependentState, dependentEntry2a.State);
- Assert.Equal(expectedDependentState, dependentEntry2b.State);
-
- Assert.Equal(1, sweet.Id);
- Assert.Equal(1, dependentEntry.Property(dependentEntry.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- Assert.Equal(1, dependentEntry2a.Property(dependentEntry2a.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- Assert.Equal(1, dependentEntry2b.Property(dependentEntry2b.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- }
-
- [ConditionalTheory]
- [InlineData(false, false)]
- [InlineData(false, true)]
- [InlineData(true, false)]
- [InlineData(true, true)]
- public void Can_attach_owned_dependent_with_reference_to_parent(bool useAttach, bool setDependentKey)
- {
- using var context = new EarlyLearningCenter();
- var dreams = new Dreams
- {
- Sweet = new Sweet { Id = 1 },
- Are = new AreMade(),
- Made = new AreMade()
- };
-
- if (setDependentKey)
- {
- var dreamsEntry = context.Entry(dreams);
- dreamsEntry.Property("SweetId").CurrentValue = 1;
- dreamsEntry.Reference(e => e.Are).TargetEntry.Property("DreamsSweetId").CurrentValue = 1;
- dreamsEntry.Reference(e => e.Made).TargetEntry.Property("DreamsSweetId").CurrentValue = 1;
- }
-
- if (useAttach)
- {
- context.Attach(dreams);
- }
- else
- {
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- dreams, e =>
- {
- e.Entry.State = e.Entry.IsKeySet ? EntityState.Unchanged : EntityState.Added;
-
- traversal.Add(NodeString(e));
- });
-
- Assert.Equal(
- new List
- {
- " -----> Dreams:1",
- "Dreams:1 ---Are--> Dreams.Are#AreMade:1",
- "Dreams:1 ---Made--> Dreams.Made#AreMade:1",
- "Dreams:1 ---Sweet--> Sweet:1"
- },
- traversal);
- }
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- var dependentEntry = context.Entry(dreams);
- var dependentEntry2a = context.Entry(dreams.Are);
- var dependentEntry2b = context.Entry(dreams.Made);
-
- var expectedPrincipalState = EntityState.Unchanged;
- var expectedDependentState = setDependentKey ? EntityState.Unchanged : EntityState.Added;
-
- Assert.Equal(expectedPrincipalState, context.Entry(dreams.Sweet).State);
- Assert.Equal(expectedDependentState, dependentEntry.State);
- Assert.Equal(expectedDependentState, dependentEntry2a.State);
- Assert.Equal(expectedDependentState, dependentEntry2b.State);
-
- Assert.Equal(1, dreams.Sweet.Id);
- Assert.Equal(1, dependentEntry.Property(dependentEntry.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- Assert.Equal(1, dependentEntry2a.Property(dependentEntry2a.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- Assert.Equal(1, dependentEntry2b.Property(dependentEntry2b.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- }
-
- [ConditionalFact]
- public void Can_attach_parent_with_child_collection()
- {
- using var context = new EarlyLearningCenter();
- var category = new Category
- {
- Id = 1,
- Products = new List
- {
- new Product { Id = 1 },
- new Product { Id = 2 },
- new Product { Id = 3 }
- }
- };
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- category, e =>
- {
- traversal.Add(NodeString(e));
- e.Entry.State = EntityState.Modified;
- });
-
- Assert.Equal(
- new List
- {
- " -----> Category:1",
- "Category:1 ---Products--> Product:1",
- "Category:1 ---Products--> Product:2",
- "Category:1 ---Products--> Product:3"
- },
- traversal);
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- Assert.Equal(EntityState.Modified, context.Entry(category).State);
- Assert.Equal(EntityState.Modified, context.Entry(category.Products[0]).State);
- Assert.Equal(EntityState.Modified, context.Entry(category.Products[1]).State);
- Assert.Equal(EntityState.Modified, context.Entry(category.Products[2]).State);
-
- Assert.Same(category, category.Products[0].Category);
- Assert.Same(category, category.Products[1].Category);
- Assert.Same(category, category.Products[2].Category);
-
- Assert.Equal(category.Id, category.Products[0].CategoryId);
- Assert.Equal(category.Id, category.Products[1].CategoryId);
- Assert.Equal(category.Id, category.Products[2].CategoryId);
- }
-
- [ConditionalFact]
- public void Can_attach_child_with_reference_to_parent()
- {
- using var context = new EarlyLearningCenter();
- var product = new Product { Id = 1, Category = new Category { Id = 1 } };
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- product, e =>
- {
- traversal.Add(NodeString(e));
- e.Entry.State = EntityState.Modified;
- });
-
- Assert.Equal(
- new List { " -----> Product:1", "Product:1 ---Category--> Category:1" },
- traversal);
-
- Assert.Equal(2, context.ChangeTracker.Entries().Count());
-
- Assert.Equal(EntityState.Modified, context.Entry(product).State);
- Assert.Equal(EntityState.Modified, context.Entry(product.Category).State);
-
- Assert.Same(product, product.Category.Products[0]);
- Assert.Equal(product.Category.Id, product.CategoryId);
- }
-
- [ConditionalFact]
- public void Can_attach_parent_with_one_to_one_children()
- {
- using var context = new EarlyLearningCenter();
- var product = new Product { Id = 1, Details = new ProductDetails { Id = 1, Tag = new ProductDetailsTag { Id = 1 } } };
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- product, e =>
- {
- traversal.Add(NodeString(e));
- e.Entry.State = EntityState.Unchanged;
- });
-
- Assert.Equal(
- new List
- {
- " -----> Product:1",
- "Product:1 ---Details--> ProductDetails:1",
- "ProductDetails:1 ---Tag--> ProductDetailsTag:1"
- },
- traversal);
-
- Assert.Equal(3, context.ChangeTracker.Entries().Count());
-
- Assert.Equal(EntityState.Unchanged, context.Entry(product).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(product.Details).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(product.Details.Tag).State);
-
- Assert.Same(product, product.Details.Product);
- Assert.Same(product.Details, product.Details.Tag.Details);
- }
-
- [ConditionalFact]
- public void Can_attach_child_with_one_to_one_parents()
- {
- using var context = new EarlyLearningCenter();
- var tag = new ProductDetailsTag { Id = 1, Details = new ProductDetails { Id = 1, Product = new Product { Id = 1 } } };
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- tag, e =>
- {
- traversal.Add(NodeString(e));
- e.Entry.State = EntityState.Unchanged;
- });
-
- Assert.Equal(
- new List
- {
- " -----> ProductDetailsTag:1",
- "ProductDetailsTag:1 ---Details--> ProductDetails:1",
- "ProductDetails:1 ---Product--> Product:1"
- },
- traversal);
-
- Assert.Equal(3, context.ChangeTracker.Entries().Count());
-
- Assert.Equal(EntityState.Unchanged, context.Entry(tag).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(tag.Details).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(tag.Details.Product).State);
-
- Assert.Same(tag, tag.Details.Tag);
- Assert.Same(tag.Details, tag.Details.Product.Details);
- }
-
- [ConditionalFact]
- public void Can_attach_entity_with_one_to_one_parent_and_child()
- {
- using var context = new EarlyLearningCenter();
- var details = new ProductDetails
- {
- Id = 1,
- Product = new Product { Id = 1 },
- Tag = new ProductDetailsTag { Id = 1 }
- };
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- details, e =>
- {
- traversal.Add(NodeString(e));
- e.Entry.State = EntityState.Unchanged;
- });
-
- Assert.Equal(
- new List
- {
- " -----> ProductDetails:1",
- "ProductDetails:1 ---Product--> Product:1",
- "ProductDetails:1 ---Tag--> ProductDetailsTag:1"
- },
- traversal);
-
- Assert.Equal(3, context.ChangeTracker.Entries().Count());
-
- Assert.Equal(EntityState.Unchanged, context.Entry(details).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(details.Product).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(details.Tag).State);
-
- Assert.Same(details, details.Tag.Details);
- Assert.Same(details, details.Product.Details);
- }
-
- [ConditionalFact]
- public void Entities_that_are_already_tracked_will_not_get_attached()
- {
- using var context = new EarlyLearningCenter();
- var existingProduct = context.Attach(
- new Product { Id = 2, CategoryId = 1 }).Entity;
-
- var category = new Category
- {
- Id = 1,
- Products = new List
- {
- new Product { Id = 1 },
- existingProduct,
- new Product { Id = 3 }
- }
- };
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- category, e =>
- {
- traversal.Add(NodeString(e));
- e.Entry.State = EntityState.Modified;
- });
-
- Assert.Equal(
- new List
- {
- " -----> Category:1",
- "Category:1 ---Products--> Product:1",
- "Category:1 ---Products--> Product:3"
- },
- traversal);
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- Assert.Equal(EntityState.Modified, context.Entry(category).State);
- Assert.Equal(EntityState.Modified, context.Entry(category.Products[0]).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[1]).State);
- Assert.Equal(EntityState.Modified, context.Entry(category.Products[2]).State);
-
- Assert.Same(category, category.Products[0].Category);
- Assert.Same(category, category.Products[1].Category);
- Assert.Same(category, category.Products[2].Category);
-
- Assert.Equal(category.Id, category.Products[0].CategoryId);
- Assert.Equal(category.Id, category.Products[1].CategoryId);
- Assert.Equal(category.Id, category.Products[2].CategoryId);
- }
-
- [ConditionalFact]
- public void Further_graph_traversal_stops_if_an_entity_is_not_attached()
- {
- using var context = new EarlyLearningCenter();
- var category = new Category
- {
- Id = 1,
- Products = new List
- {
- new Product
- {
- Id = 1,
- CategoryId = 1,
- Details = new ProductDetails { Id = 1 }
- },
- new Product
- {
- Id = 2,
- CategoryId = 1,
- Details = new ProductDetails { Id = 2 }
- },
- new Product
- {
- Id = 3,
- CategoryId = 1,
- Details = new ProductDetails { Id = 3 }
- }
- }
- };
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- category, e =>
- {
- traversal.Add(NodeString(e));
- if (!(e.Entry.Entity is Product product)
- || product.Id != 2)
- {
- e.Entry.State = EntityState.Unchanged;
- }
- });
-
- Assert.Equal(
- new List
- {
- " -----> Category:1",
- "Category:1 ---Products--> Product:1",
- "Product:1 ---Details--> ProductDetails:1",
- "Category:1 ---Products--> Product:2",
- "Category:1 ---Products--> Product:3",
- "Product:3 ---Details--> ProductDetails:3"
- },
- traversal);
-
- Assert.Equal(5, context.ChangeTracker.Entries().Count(e => e.State != EntityState.Detached));
-
- Assert.Equal(EntityState.Unchanged, context.Entry(category).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[0]).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[0].Details).State);
- Assert.Equal(EntityState.Detached, context.Entry(category.Products[1]).State);
- Assert.Equal(EntityState.Detached, context.Entry(category.Products[1].Details).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[2]).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[2].Details).State);
-
- Assert.Same(category, category.Products[0].Category);
- Assert.Null(category.Products[1].Category);
- Assert.Same(category, category.Products[2].Category);
-
- Assert.Equal(category.Id, category.Products[0].CategoryId);
- Assert.Equal(category.Id, category.Products[1].CategoryId);
- Assert.Equal(category.Id, category.Products[2].CategoryId);
-
- Assert.Same(category.Products[0], category.Products[0].Details.Product);
- Assert.Null(category.Products[1].Details.Product);
- Assert.Same(category.Products[2], category.Products[2].Details.Product);
- }
-
- [ConditionalFact]
- public void Graph_iterator_does_not_go_visit_Apple()
- {
- using var context = new EarlyLearningCenter();
- var details = new ProductDetails { Id = 1, Product = new Product { Id = 1 } };
- details.Product.Details = details;
-
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(details, e => traversal.Add(NodeString(e)));
-
- Assert.Equal(
- new List { " -----> ProductDetails:1" },
- traversal);
-
- Assert.Equal(0, context.ChangeTracker.Entries().Count(e => e.State != EntityState.Detached));
- }
-
- [ConditionalFact]
- public void Can_attach_parent_with_some_new_and_some_existing_entities()
- {
- KeyValueAttachTest(
- (category, changeTracker) =>
- {
- var traversal = new List();
-
- changeTracker.TrackGraph(
- category,
- e =>
- {
- traversal.Add(NodeString(e));
- e.Entry.State = e.Entry.Entity is Product product && product.Id == 0
- ? EntityState.Added
- : EntityState.Unchanged;
- });
-
- Assert.Equal(
- new List
- {
- " -----> Category:77",
- "Category:77 ---Products--> Product:77",
- "Category:77 ---Products--> Product:0",
- "Category:77 ---Products--> Product:78"
- },
- traversal);
- });
- }
-
- [ConditionalFact]
- public void Can_attach_graph_using_built_in_tracker()
- {
- var tracker = new KeyValueEntityTracker(updateExistingEntities: false);
-
- KeyValueAttachTest((category, changeTracker) => changeTracker.TrackGraph(category, tracker.TrackEntity));
- }
-
- [ConditionalFact]
- public void Can_update_graph_using_built_in_tracker()
- {
- var tracker = new KeyValueEntityTracker(updateExistingEntities: true);
-
- KeyValueAttachTest((category, changeTracker) => changeTracker.TrackGraph(category, tracker.TrackEntity), expectModified: true);
- }
-
- private static void KeyValueAttachTest(Action tracker, bool expectModified = false)
- {
- using var context = new EarlyLearningCenter();
- var category = new Category
- {
- Id = 77,
- Products = new List
- {
- new Product { Id = 77, CategoryId = expectModified ? 0 : 77 },
- new Product { Id = 0, CategoryId = expectModified ? 0 : 77 },
- new Product { Id = 78, CategoryId = expectModified ? 0 : 77 }
- }
- };
-
- tracker(category, context.ChangeTracker);
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- var nonAddedState = expectModified ? EntityState.Modified : EntityState.Unchanged;
-
- Assert.Equal(nonAddedState, context.Entry(category).State);
- Assert.Equal(nonAddedState, context.Entry(category.Products[0]).State);
- Assert.Equal(EntityState.Added, context.Entry(category.Products[1]).State);
- Assert.Equal(nonAddedState, context.Entry(category.Products[2]).State);
-
- Assert.Equal(77, category.Products[0].Id);
- Assert.Equal(1, category.Products[1].Id);
- Assert.Equal(78, category.Products[2].Id);
-
- Assert.Same(category, category.Products[0].Category);
- Assert.Same(category, category.Products[1].Category);
- Assert.Same(category, category.Products[2].Category);
-
- Assert.Equal(category.Id, category.Products[0].CategoryId);
- Assert.Equal(category.Id, category.Products[1].CategoryId);
- Assert.Equal(category.Id, category.Products[2].CategoryId);
- }
-
- [ConditionalFact]
- public void Can_attach_graph_using_custom_delegate()
- {
- var tracker = new MyTracker(updateExistingEntities: false);
-
- using var context = new EarlyLearningCenter();
- var category = new Category
- {
- Id = 77,
- Products = new List
- {
- new Product { Id = 77, CategoryId = 77 },
- new Product { Id = 0, CategoryId = 77 },
- new Product { Id = 78, CategoryId = 77 }
- }
- };
-
- context.ChangeTracker.TrackGraph(category, tracker.TrackEntity);
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- Assert.Equal(EntityState.Unchanged, context.Entry(category).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[0]).State);
- Assert.Equal(EntityState.Added, context.Entry(category.Products[1]).State);
- Assert.Equal(EntityState.Unchanged, context.Entry(category.Products[2]).State);
-
- Assert.Equal(77, category.Products[0].Id);
- Assert.Equal(777, category.Products[1].Id);
- Assert.Equal(78, category.Products[2].Id);
-
- Assert.Same(category, category.Products[0].Category);
- Assert.Same(category, category.Products[1].Category);
- Assert.Same(category, category.Products[2].Category);
-
- Assert.Equal(category.Id, category.Products[0].CategoryId);
- Assert.Equal(category.Id, category.Products[1].CategoryId);
- Assert.Equal(category.Id, category.Products[2].CategoryId);
- }
-
- private class MyTracker : KeyValueEntityTracker
- {
- public MyTracker(bool updateExistingEntities)
- : base(updateExistingEntities)
- {
- }
-
- public override EntityState DetermineState(EntityEntry entry)
- {
- if (!entry.IsKeySet)
- {
- entry.GetInfrastructure()[entry.Metadata.FindPrimaryKey().Properties.Single()] = 777;
- return EntityState.Added;
- }
-
- return base.DetermineState(entry);
- }
- }
-
- [ConditionalTheory]
- [InlineData(false, false)]
- [InlineData(false, true)]
- [InlineData(true, false)]
- [InlineData(true, true)]
- public void Can_add_owned_dependent_with_reference_to_parent(bool useAdd, bool setDependentKey)
- {
- using var context = new EarlyLearningCenter();
- var dreams = new Dreams
- {
- Sweet = new Sweet { Id = 1 },
- Are = new AreMade(),
- Made = new AreMade()
- };
-
- context.Entry(dreams.Sweet).State = EntityState.Unchanged;
-
- if (setDependentKey)
- {
- var dreamsEntry = context.Entry(dreams);
- dreamsEntry.Property("SweetId").CurrentValue = 1;
- dreamsEntry.Reference(e => e.Are).TargetEntry.Property("DreamsSweetId").CurrentValue = 1;
- dreamsEntry.Reference(e => e.Made).TargetEntry.Property("DreamsSweetId").CurrentValue = 1;
- }
-
- if (useAdd)
- {
- context.Add(dreams);
- }
- else
- {
- var traversal = new List();
-
- context.ChangeTracker.TrackGraph(
- dreams, e =>
- {
- e.Entry.State = e.Entry.IsKeySet && !e.Entry.Metadata.IsOwned()
- ? EntityState.Unchanged
- : EntityState.Added;
-
- traversal.Add(NodeString(e));
- });
-
- Assert.Equal(
- new List
- {
- " -----> Dreams:1",
- "Dreams:1 ---Are--> Dreams.Are#AreMade:1",
- "Dreams:1 ---Made--> Dreams.Made#AreMade:1"
- },
- traversal);
- }
-
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- var dependentEntry = context.Entry(dreams);
- var dependentEntry2a = context.Entry(dreams.Are);
- var dependentEntry2b = context.Entry(dreams.Made);
-
- var expectedPrincipalState = EntityState.Unchanged;
- var expectedDependentState = EntityState.Added;
-
- Assert.Equal(expectedPrincipalState, context.Entry(dreams.Sweet).State);
- Assert.Equal(expectedDependentState, dependentEntry.State);
- Assert.Equal(expectedDependentState, dependentEntry2a.State);
- Assert.Equal(expectedDependentState, dependentEntry2b.State);
-
- Assert.Equal(1, dreams.Sweet.Id);
- Assert.Equal(1, dependentEntry.Property(dependentEntry.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- Assert.Equal(1, dependentEntry2a.Property(dependentEntry2a.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- Assert.Equal(1, dependentEntry2b.Property(dependentEntry2b.Metadata.FindPrimaryKey().Properties[0].Name).CurrentValue);
- }
-
[ConditionalTheory] // Issue #17828
[InlineData(false)]
[InlineData(true)]
@@ -2173,161 +1337,6 @@ public void DetectChanges_reparents_even_when_immediate_cascade_enabled(bool del
Assert.Equal(EntityState.Modified, context.Entry(child).State);
}
- [ConditionalTheory] // Issue #12590
- [InlineData(false, false)]
- [InlineData(false, true)]
- [InlineData(true, false)]
- [InlineData(true, true)]
- public void Dependents_are_detached_not_deleted_when_principal_is_detached(bool delayCascade, bool trackNewDependents)
- {
- using var context = new EarlyLearningCenter();
-
- var category = new Category
- {
- Id = 1,
- Products = new List
- {
- new Product { Id = 1 },
- new Product { Id = 2 },
- new Product { Id = 3 }
- }
- };
-
- context.Attach(category);
-
- var categoryEntry = context.Entry(category);
- var product0Entry = context.Entry(category.Products[0]);
- var product1Entry = context.Entry(category.Products[1]);
- var product2Entry = context.Entry(category.Products[2]);
-
- Assert.Equal(EntityState.Unchanged, categoryEntry.State);
- Assert.Equal(EntityState.Unchanged, product0Entry.State);
- Assert.Equal(EntityState.Unchanged, product1Entry.State);
- Assert.Equal(EntityState.Unchanged, product2Entry.State);
-
- if (delayCascade)
- {
- context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
- }
-
- context.Entry(category).State = EntityState.Detached;
-
- Assert.Equal(EntityState.Detached, categoryEntry.State);
-
- if (delayCascade)
- {
- Assert.Equal(EntityState.Unchanged, product0Entry.State);
- Assert.Equal(EntityState.Unchanged, product1Entry.State);
- Assert.Equal(EntityState.Unchanged, product2Entry.State);
- }
- else
- {
- Assert.Equal(EntityState.Detached, product0Entry.State);
- Assert.Equal(EntityState.Detached, product1Entry.State);
- Assert.Equal(EntityState.Detached, product2Entry.State);
- }
-
- var newCategory = new Category { Id = 1, };
-
- if (trackNewDependents)
- {
- newCategory.Products = new List
- {
- new Product { Id = 1 },
- new Product { Id = 2 },
- new Product { Id = 3 }
- };
- }
-
- var traversal = new List();
-
- if (delayCascade && trackNewDependents)
- {
- Assert.Equal(
- CoreStrings.IdentityConflict(nameof(Product), "{'Id'}"),
- Assert.Throws(TrackGraph).Message);
- }
- else
- {
- TrackGraph();
-
- Assert.Equal(
- trackNewDependents
- ? new List
- {
- " -----> Category:1",
- "Category:1 ---Products--> Product:1",
- "Category:1 ---Products--> Product:2",
- "Category:1 ---Products--> Product:3"
- }
- : new List
- {
- " -----> Category:1"
- },
- traversal);
-
- if (trackNewDependents || delayCascade)
- {
- Assert.Equal(4, context.ChangeTracker.Entries().Count());
-
- categoryEntry = context.Entry(newCategory);
- product0Entry = context.Entry(newCategory.Products[0]);
- product1Entry = context.Entry(newCategory.Products[1]);
- product2Entry = context.Entry(newCategory.Products[2]);
-
- Assert.Equal(EntityState.Modified, categoryEntry.State);
-
- if (trackNewDependents)
- {
- Assert.Equal(EntityState.Modified, product0Entry.State);
- Assert.Equal(EntityState.Modified, product1Entry.State);
- Assert.Equal(EntityState.Modified, product2Entry.State);
-
- Assert.NotSame(newCategory.Products[0], category.Products[0]);
- Assert.NotSame(newCategory.Products[1], category.Products[1]);
- Assert.NotSame(newCategory.Products[2], category.Products[2]);
- }
- else
- {
- Assert.Equal(EntityState.Unchanged, product0Entry.State);
- Assert.Equal(EntityState.Unchanged, product1Entry.State);
- Assert.Equal(EntityState.Unchanged, product2Entry.State);
-
- Assert.Same(newCategory.Products[0], category.Products[0]);
- Assert.Same(newCategory.Products[1], category.Products[1]);
- Assert.Same(newCategory.Products[2], category.Products[2]);
- }
-
- Assert.Same(newCategory, newCategory.Products[0].Category);
- Assert.Same(newCategory, newCategory.Products[1].Category);
- Assert.Same(newCategory, newCategory.Products[2].Category);
-
- Assert.Equal(newCategory.Id, product0Entry.Property("CategoryId").CurrentValue);
- Assert.Equal(newCategory.Id, product1Entry.Property("CategoryId").CurrentValue);
- Assert.Equal(newCategory.Id, product2Entry.Property("CategoryId").CurrentValue);
- }
- else
- {
- Assert.Single(context.ChangeTracker.Entries());
-
- categoryEntry = context.Entry(newCategory);
-
- Assert.Equal(EntityState.Modified, categoryEntry.State);
- Assert.Null(newCategory.Products);
- }
- }
-
- void TrackGraph()
- {
- context.ChangeTracker.TrackGraph(
- newCategory, n =>
- {
- n.Entry.State = EntityState.Modified;
- traversal.Add(NodeString(n));
- });
- }
- }
-
[ConditionalTheory] // Issue #19203
[InlineData(false, false)]
[InlineData(false, true)]
@@ -3026,163 +2035,6 @@ public void Explicitly_calling_DetectChanges_works_even_if_auto_DetectChanges_is
Assert.Equal(EntityState.Modified, entry.State);
}
- [ConditionalFact]
- public void TrackGraph_does_not_call_DetectChanges()
- {
- var provider =
- InMemoryTestHelpers.Instance.CreateServiceProvider(
- new ServiceCollection().AddScoped());
- using var context = new EarlyLearningCenter(provider);
- var changeDetector = (ChangeDetectorProxy)context.GetService();
-
- changeDetector.DetectChangesCalled = false;
-
- context.ChangeTracker.TrackGraph(CreateSimpleGraph(2), e => e.Entry.State = EntityState.Unchanged);
-
- Assert.False(changeDetector.DetectChangesCalled);
-
- context.ChangeTracker.DetectChanges();
-
- Assert.True(changeDetector.DetectChangesCalled);
- }
-
- [ConditionalFact]
- public void TrackGraph_overload_can_visit_an_already_attached_graph()
- {
- using var context = new EarlyLearningCenter();
- var category = new Category
- {
- Id = 1,
- Products = new List
- {
- new Product
- {
- Id = 1,
- CategoryId = 1,
- Details = new ProductDetails { Id = 1 }
- },
- new Product
- {
- Id = 2,
- CategoryId = 1,
- Details = new ProductDetails { Id = 2 }
- },
- new Product
- {
- Id = 3,
- CategoryId = 1,
- Details = new ProductDetails { Id = 3 }
- }
- }
- };
-
- context.Attach(category);
-
- var visited = new HashSet