From 91dbf3f1eefb79bd02c870ebe4ae8aa7ce8736db Mon Sep 17 00:00:00 2001 From: Alexander Zabluda Date: Wed, 8 Apr 2015 20:34:19 +0200 Subject: [PATCH 1/4] Unit test 'MergeChanges_Should_Set_TrackingState_To_Unchanged_For_Added_Order_With_Null_Customer' fails for derived Order --- .../NorthwindModels/PriorityOrder.cs | 21 +++++++++++++++++++ ...kableEntities.Client.Tests.Entities.csproj | 1 + .../ChangeTrackingExtensionsTests.cs | 3 ++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Source/Tests/TrackableEntities.Client.Tests.Entities/NorthwindModels/PriorityOrder.cs diff --git a/Source/Tests/TrackableEntities.Client.Tests.Entities/NorthwindModels/PriorityOrder.cs b/Source/Tests/TrackableEntities.Client.Tests.Entities/NorthwindModels/PriorityOrder.cs new file mode 100644 index 00000000..0a0118dd --- /dev/null +++ b/Source/Tests/TrackableEntities.Client.Tests.Entities/NorthwindModels/PriorityOrder.cs @@ -0,0 +1,21 @@ +using System; +using Newtonsoft.Json; + +namespace TrackableEntities.Client.Tests.Entities.NorthwindModels +{ + [JsonObject(IsReference = true)] + public class PriorityOrder : Order + { + private string _priorityPlan; + public string PriorityPlan + { + get { return _priorityPlan; } + set + { + if (value == _priorityPlan) return; + _priorityPlan = value; + NotifyPropertyChanged(() => PriorityPlan); + } + } + } +} diff --git a/Source/Tests/TrackableEntities.Client.Tests.Entities/TrackableEntities.Client.Tests.Entities.csproj b/Source/Tests/TrackableEntities.Client.Tests.Entities/TrackableEntities.Client.Tests.Entities.csproj index 7fbc899b..243b33f0 100644 --- a/Source/Tests/TrackableEntities.Client.Tests.Entities/TrackableEntities.Client.Tests.Entities.csproj +++ b/Source/Tests/TrackableEntities.Client.Tests.Entities/TrackableEntities.Client.Tests.Entities.csproj @@ -53,6 +53,7 @@ + diff --git a/Source/Tests/TrackableEntities.Client.Tests.Extensions/ChangeTrackingExtensionsTests.cs b/Source/Tests/TrackableEntities.Client.Tests.Extensions/ChangeTrackingExtensionsTests.cs index 23eb9e3c..924f7d7a 100644 --- a/Source/Tests/TrackableEntities.Client.Tests.Extensions/ChangeTrackingExtensionsTests.cs +++ b/Source/Tests/TrackableEntities.Client.Tests.Extensions/ChangeTrackingExtensionsTests.cs @@ -122,8 +122,9 @@ public void MergeChanges_Should_Set_TrackingState_To_Unchanged_For_Added_Order_W { // Arrange var database = new MockNorthwind(); - var origOrder = new Order + var origOrder = new PriorityOrder { + PriorityPlan = "Silver", OrderDate = DateTime.Parse("1996-07-04"), CustomerId = "ALFKI" }; From 890b23a12f031f5e37638368d3fc499148043ed0 Mon Sep 17 00:00:00 2001 From: Alexander Zabluda Date: Wed, 8 Apr 2015 21:06:20 +0200 Subject: [PATCH 2/4] SetEntityProperties must look into base classes when reflecting entity's properties This fixes unit test 'MergeChanges_Should_Set_TrackingState_To_Unchanged_For_Added_Order_With_Null_Customer' --- Source/TrackableEntities.Client/ChangeTrackingExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/TrackableEntities.Client/ChangeTrackingExtensions.cs b/Source/TrackableEntities.Client/ChangeTrackingExtensions.cs index 36b80596..ddd58c09 100644 --- a/Source/TrackableEntities.Client/ChangeTrackingExtensions.cs +++ b/Source/TrackableEntities.Client/ChangeTrackingExtensions.cs @@ -221,7 +221,7 @@ private static void SetEntityProperties(this ITrackable targetItem, ITrackable s #if SILVERLIGHT || NET40 foreach (var prop in targetItem.GetType().GetProperties().Where(p => p.CanWrite) #else - foreach (var prop in targetItem.GetType().GetTypeInfo().DeclaredProperties + foreach (var prop in targetItem.GetType().BaseTypes().SelectMany(t => t.GetTypeInfo().DeclaredProperties) .Where(p => p.CanWrite && !p.GetMethod.IsPrivate) #endif .Except(targetItem.GetNavigationProperties(false).Select(np => np.Property))) From b1fd3564504a40354f4021a12c6d01fb717942fb Mon Sep 17 00:00:00 2001 From: Alexander Zabluda Date: Fri, 10 Apr 2015 13:27:01 +0200 Subject: [PATCH 3/4] New failing unit test 'Apply_Changes_Should_Mark_Unchanged_Product_Of_Added_OrderDetail_Of_Added_Order_As_Unchanged' --- .../NorthwindDbContextTests.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs b/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs index 8940a409..6898aedf 100644 --- a/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs +++ b/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs @@ -401,6 +401,26 @@ public void Apply_Changes_Should_Mark_Order_Modified_With_OrderDetails_Added_Mod Assert.AreEqual(EntityState.Unchanged, context.Entry(detail4).State); } + [Test] + public void Apply_Changes_Should_Mark_Unchanged_Product_Of_Added_OrderDetail_Of_Added_Order_As_Unchanged() + { + // Arrange + var context = TestsHelper.CreateNorthwindDbContext(CreateNorthwindDbOptions); + var order = new MockNorthwind().Orders[0]; + var orderDetail = order.OrderDetails[0]; + var product = orderDetail.Product; + order.TrackingState = TrackingState.Added; + orderDetail.TrackingState = TrackingState.Added; + + // Act + context.ApplyChanges(order); + + // Assert + Assert.AreEqual(EntityState.Added, context.Entry(order).State); + Assert.AreEqual(EntityState.Added, context.Entry(orderDetail).State); + Assert.AreEqual(EntityState.Unchanged, context.Entry(product).State); + } + [Test] public void Apply_Changes_Should_Mark_Unchanged_Order_With_Multiple_OrderDetails_Added() { From 9fd5528f37efb03dfe7319d1a8b961c3f0396d6c Mon Sep 17 00:00:00 2001 From: Anthony Sneed Date: Mon, 20 Apr 2015 17:16:57 +0200 Subject: [PATCH 4/4] Updated ApplyChanges to fix failing test: Apply_Changes_Should_Mark_Unchanged_Product_Of_Added_OrderDetail_Of_Added_Order_As_Unchanged. If item or parent is marked as Added (1-M or 1-1 relation), set state to Added on item, then apply changed on item properties, which sets reference properties back from Added (set by EF recursively) to that indicated by TrackingState (unchanged, modified). --- .../NorthwindDbContextTests.cs | 1 + .../DbContextExtensions.cs | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs b/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs index 6898aedf..24d4aa34 100644 --- a/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs +++ b/Source/Tests/TrackableEntities.EF.5.Tests/NorthwindDbContextTests.cs @@ -399,6 +399,7 @@ public void Apply_Changes_Should_Mark_Order_Modified_With_OrderDetails_Added_Mod Assert.AreEqual(EntityState.Modified, context.Entry(detail2).State); Assert.AreEqual(EntityState.Deleted, context.Entry(detail3).State); Assert.AreEqual(EntityState.Unchanged, context.Entry(detail4).State); + Assert.AreEqual(EntityState.Unchanged, context.Entry(detail1.Product).State); } [Test] diff --git a/Source/TrackableEntities.EF.5/DbContextExtensions.cs b/Source/TrackableEntities.EF.5/DbContextExtensions.cs index 2154a50d..4ad43fc0 100644 --- a/Source/TrackableEntities.EF.5/DbContextExtensions.cs +++ b/Source/TrackableEntities.EF.5/DbContextExtensions.cs @@ -97,13 +97,11 @@ private static void ApplyChanges(this DbContext context, // Set state for child collections context.ApplyChangesOnProperties(item, visitationHelper); return; - } + } - // Exit if parent is added or deleted, - // and it's not a M-1 relation + // Exit if parent is deleted and not a M-1 relation if (parent != null - && (parent.TrackingState == TrackingState.Added - || parent.TrackingState == TrackingState.Deleted) + && (parent.TrackingState == TrackingState.Deleted) && !context.IsRelatedProperty(parent.GetType(), propertyName, RelationshipType.ManyToOne)) return; @@ -126,9 +124,14 @@ private static void ApplyChanges(this DbContext context, return; } - // Set state to Added on parent only + // Set state to Added if item marked as Added if (item.TrackingState == TrackingState.Added - && (state == null || state == TrackingState.Added)) + && (state == null || state == TrackingState.Added) + // Or if parent marked as Added and 1-M or 1-1 relation + || (parent != null + && parent.TrackingState == TrackingState.Added + && (context.IsRelatedProperty(parent.GetType(), propertyName, RelationshipType.OneToMany) + || context.IsRelatedProperty(parent.GetType(), propertyName, RelationshipType.OneToOne)))) { context.Entry(item).State = EntityState.Added; context.ApplyChangesOnProperties(item, visitationHelper); @@ -565,6 +568,9 @@ private static bool IsRelatedProperty(this DbContext dbContext, case RelationshipType.ManyToMany: return navProp.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many && navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many; + case RelationshipType.OneToMany: + return navProp.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One + && navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many; default: return false; } @@ -603,6 +609,7 @@ enum RelationshipType ManyToOne, OneToOne, ManyToMany, + OneToMany } #endregion