Fixes issue of observable collections, event binding and deep cloning

This commit is contained in:
Shannon
2018-10-19 08:26:17 +11:00
parent c3e2d2a821
commit b30f55ea30
3 changed files with 59 additions and 2 deletions

View File

@@ -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

View File

@@ -113,7 +113,11 @@ namespace Umbraco.Core.Models
/// <summary>
/// Gets or sets the collection of properties for the entity.
/// </summary>
/// <remarks>
/// Marked DoNotClone since we'll manually clone the underlying field to deal with the event handling
/// </remarks>
[DataMember]
[DoNotClone]
public virtual PropertyCollection Properties
{
get => _properties;
@@ -477,5 +481,39 @@ namespace Umbraco.Core.Models
}
#endregion
/// <summary>
/// Override to deal with specific object instances
/// </summary>
/// <returns></returns>
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;
}
}
}

View File

@@ -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<PropertySelectors> Ps = new Lazy<PropertySelectors>();
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);