diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index fefbdfbef7..92b0488a82 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -438,6 +438,7 @@ namespace Umbraco.Core.Models _contentType = contentType; ContentTypeBase = contentType; Properties.EnsurePropertyTypes(PropertyTypes); + //TODO: Shouldn't we remove this event handler first before re-adding it in case the handler already exists Properties.CollectionChanged += PropertiesChanged; } @@ -455,6 +456,7 @@ namespace Umbraco.Core.Models _contentType = contentType; ContentTypeBase = contentType; Properties.EnsureCleanPropertyTypes(PropertyTypes); + //TODO: Shouldn't we remove this event handler first before re-adding it in case the handler already exists Properties.CollectionChanged += PropertiesChanged; return; } @@ -500,10 +502,18 @@ namespace Umbraco.Core.Models public override object DeepClone() { var clone = (Content) base.DeepClone(); + //turn off change tracking clone.DisableChangeTracking(); - //need to manually clone this since it's not settable - clone._contentType = (IContentType)ContentType.DeepClone(); + + //if culture infos exist then deal with event bindings + if (clone._publishInfos != null) + { + clone._publishInfos.CollectionChanged -= this.PublishNamesCollectionChanged; //clear this event handler if any + clone._publishInfos = (CultureNameCollection)_publishInfos.DeepClone(); //manually deep clone + clone._publishInfos.CollectionChanged += clone.PublishNamesCollectionChanged; //re-assign correct event handler + } + //this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); //re-enable tracking diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 2336188c50..1c5ee18be6 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -113,7 +113,11 @@ namespace Umbraco.Core.Models /// /// Gets or sets the collection of properties for the entity. /// + /// + /// Marked DoNotClone since we'll manually clone the underlying field to deal with the event handling + /// [DataMember] + [DoNotClone] public virtual PropertyCollection Properties { get => _properties; @@ -477,5 +481,39 @@ namespace Umbraco.Core.Models } #endregion + + /// + /// Override to deal with specific object instances + /// + /// + public override object DeepClone() + { + var clone = (ContentBase)base.DeepClone(); + //turn off change tracking + clone.DisableChangeTracking(); + + //if culture infos exist then deal with event bindings + if (clone._cultureInfos != null) + { + clone._cultureInfos.CollectionChanged -= this.CultureNamesCollectionChanged; //clear this event handler if any + clone._cultureInfos = (CultureNameCollection)_cultureInfos.DeepClone(); //manually deep clone + clone._cultureInfos.CollectionChanged += clone.CultureNamesCollectionChanged; //re-assign correct event handler + } + + //if properties exist then deal with event bindings + if (clone._properties != null) + { + clone._properties.CollectionChanged -= this.PropertiesChanged; //clear this event handler if any + clone._properties = (PropertyCollection)_properties.DeepClone(); //manually deep clone + clone._properties.CollectionChanged += clone.PropertiesChanged; //re-assign correct event handler + } + + //this shouldn't really be needed since we're not tracking + clone.ResetDirtyProperties(false); + //re-enable tracking + clone.EnableChangeTracking(); + + return clone; + } } } diff --git a/src/Umbraco.Core/Models/Entities/EntityBase.cs b/src/Umbraco.Core/Models/Entities/EntityBase.cs index ab57d57ab6..9cd6d1edad 100644 --- a/src/Umbraco.Core/Models/Entities/EntityBase.cs +++ b/src/Umbraco.Core/Models/Entities/EntityBase.cs @@ -13,6 +13,10 @@ namespace Umbraco.Core.Models.Entities [DebuggerDisplay("Id: {" + nameof(Id) + "}")] public abstract class EntityBase : BeingDirtyBase, IEntity { +#if DEBUG + public Guid InstanceId = Guid.NewGuid(); +#endif + private static readonly Lazy Ps = new Lazy(); private bool _hasIdentity; @@ -161,6 +165,11 @@ namespace Umbraco.Core.Models.Entities var unused = Key; // ensure that 'this' has a key, before cloning var clone = (EntityBase) MemberwiseClone(); +#if DEBUG + clone.InstanceId = Guid.NewGuid(); +#endif + + // clear changes (ensures the clone has its own dictionaries) // then disable change tracking clone.ResetDirtyProperties(false);