Fixes dirty tracking with resetting properties

This commit is contained in:
Shannon
2018-10-25 15:47:25 +11:00
parent 9df4a8091e
commit 42f0d2b356
14 changed files with 117 additions and 185 deletions

View File

@@ -499,28 +499,23 @@ namespace Umbraco.Core.Models
return clone;
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (Content) base.DeepClone();
base.PerformDeepClone(clone);
//turn off change tracking
clone.DisableChangeTracking();
var clonedContent = (Content)clone;
//need to manually clone this since it's not settable
clone._contentType = (IContentType) ContentType.DeepClone();
clonedContent._contentType = (IContentType) ContentType.DeepClone();
//if culture infos exist then deal with event bindings
if (clone._publishInfos != null)
if (clonedContent._publishInfos != null)
{
clone._publishInfos.CollectionChanged -= PublishNamesCollectionChanged; //clear this event handler if any
clone._publishInfos = (ContentCultureInfosCollection) _publishInfos.DeepClone(); //manually deep clone
clone._publishInfos.CollectionChanged += clone.PublishNamesCollectionChanged; //re-assign correct event handler
clonedContent._publishInfos.CollectionChanged -= PublishNamesCollectionChanged; //clear this event handler if any
clonedContent._publishInfos = (ContentCultureInfosCollection) _publishInfos.DeepClone(); //manually deep clone
clonedContent._publishInfos.CollectionChanged += clonedContent.PublishNamesCollectionChanged; //re-assign correct event handler
}
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
}
}

View File

@@ -475,33 +475,28 @@ namespace Umbraco.Core.Models
/// <remarks>
/// Overriden to deal with specific object instances
/// </remarks>
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (ContentBase) base.DeepClone();
base.PerformDeepClone(clone);
//turn off change tracking
clone.DisableChangeTracking();
var clonedContent = (ContentBase)clone;
//if culture infos exist then deal with event bindings
if (clone._cultureInfos != null)
if (clonedContent._cultureInfos != null)
{
clone._cultureInfos.CollectionChanged -= CultureInfosCollectionChanged; //clear this event handler if any
clone._cultureInfos = (ContentCultureInfosCollection) _cultureInfos.DeepClone(); //manually deep clone
clone._cultureInfos.CollectionChanged += clone.CultureInfosCollectionChanged; //re-assign correct event handler
clonedContent._cultureInfos.CollectionChanged -= CultureInfosCollectionChanged; //clear this event handler if any
clonedContent._cultureInfos = (ContentCultureInfosCollection) _cultureInfos.DeepClone(); //manually deep clone
clonedContent._cultureInfos.CollectionChanged += clonedContent.CultureInfosCollectionChanged; //re-assign correct event handler
}
//if properties exist then deal with event bindings
if (clone._properties != null)
if (clonedContent._properties != null)
{
clone._properties.CollectionChanged -= 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
clonedContent._properties.CollectionChanged -= PropertiesChanged; //clear this event handler if any
clonedContent._properties = (PropertyCollection) _properties.DeepClone(); //manually deep clone
clonedContent._properties.CollectionChanged += clonedContent.PropertiesChanged; //re-assign correct event handler
}
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
}
}

View File

@@ -467,35 +467,29 @@ namespace Umbraco.Core.Models
}
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (ContentTypeBase) base.DeepClone();
base.PerformDeepClone(clone);
//turn off change tracking
clone.DisableChangeTracking();
var clonedEntity = (ContentTypeBase) clone;
if (clone._noGroupPropertyTypes != null)
if (clonedEntity._noGroupPropertyTypes != null)
{
//need to manually wire up the event handlers for the property type collections - we've ensured
// its ignored from the auto-clone process because its return values are unions, not raw and
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
clone._noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
clone._noGroupPropertyTypes = (PropertyTypeCollection) _noGroupPropertyTypes.DeepClone(); //manually deep clone
clone._noGroupPropertyTypes.CollectionChanged += clone.PropertyTypesChanged; //re-assign correct event handler
clonedEntity._noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
clonedEntity._noGroupPropertyTypes = (PropertyTypeCollection) _noGroupPropertyTypes.DeepClone(); //manually deep clone
clonedEntity._noGroupPropertyTypes.CollectionChanged += clonedEntity.PropertyTypesChanged; //re-assign correct event handler
}
if (clone._propertyGroups != null)
if (clonedEntity._propertyGroups != null)
{
clone._propertyGroups.CollectionChanged -= PropertyGroupsChanged; //clear this event handler if any
clone._propertyGroups = (PropertyGroupCollection) _propertyGroups.DeepClone(); //manually deep clone
clone._propertyGroups.CollectionChanged += clone.PropertyGroupsChanged; //re-assign correct event handler
clonedEntity._propertyGroups.CollectionChanged -= PropertyGroupsChanged; //clear this event handler if any
clonedEntity._propertyGroups = (PropertyGroupCollection) _propertyGroups.DeepClone(); //manually deep clone
clonedEntity._propertyGroups.CollectionChanged += clonedEntity.PropertyGroupsChanged; //re-assign correct event handler
}
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
public IContentType DeepCloneWithResetIdentities(string alias)

View File

@@ -290,20 +290,15 @@ namespace Umbraco.Core.Models
.Union(ContentTypeComposition.SelectMany(x => x.CompositionIds()));
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (ContentTypeCompositionBase)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually assign since this is an internal field and will not be automatically mapped
clone.RemovedContentTypeKeyTracker = new List<int>();
clone._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
base.PerformDeepClone(clone);
return clone;
var clonedEntity = (ContentTypeCompositionBase)clone;
//need to manually assign since this is an internal field and will not be automatically mapped
clonedEntity.RemovedContentTypeKeyTracker = new List<int>();
clonedEntity._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
}
}
}

View File

@@ -104,23 +104,14 @@ namespace Umbraco.Core.Models
set { SetPropertyValueAndDetectChanges(value, ref _value, Ps.Value.ValueSelector); }
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (DictionaryTranslation)base.DeepClone();
base.PerformDeepClone(clone);
var clonedEntity = (DictionaryTranslation)clone;
// clear fields that were memberwise-cloned and that we don't want to clone
clone._language = null;
// turn off change tracking
clone.DisableChangeTracking();
// this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
// re-enable tracking
clone.EnableChangeTracking();
return clone;
clonedEntity._language = null;
}
}
}

View File

@@ -159,7 +159,7 @@ namespace Umbraco.Core.Models.Entities
}
}
public virtual object DeepClone()
public object DeepClone()
{
// memberwise-clone (ie shallow clone) the entity
var unused = Key; // ensure that 'this' has a key, before cloning
@@ -169,20 +169,29 @@ namespace Umbraco.Core.Models.Entities
clone.InstanceId = Guid.NewGuid();
#endif
// clear changes (ensures the clone has its own dictionaries)
// then disable change tracking
clone.ResetDirtyProperties(false);
//disable change tracking while we deep clone IDeepCloneable properties
clone.DisableChangeTracking();
// deep clone ref properties that are IDeepCloneable
DeepCloneHelper.DeepCloneRefProperties(this, clone);
// clear changes again (just to be sure, because we were not tracking)
// then enable change tracking
PerformDeepClone(clone);
// clear changes (ensures the clone has its own dictionaries)
clone.ResetDirtyProperties(false);
//re-enable change tracking
clone.EnableChangeTracking();
return clone;
}
/// <summary>
/// Used by inheritors to modify the DeepCloning logic
/// </summary>
/// <param name="clone"></param>
protected virtual void PerformDeepClone(object clone)
{
}
}
}

View File

@@ -156,26 +156,17 @@ namespace Umbraco.Core.Models
clone._alias = Alias;
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (File) base.DeepClone();
base.PerformDeepClone(clone);
var clonedFile = (File)clone;
// clear fields that were memberwise-cloned and that we don't want to clone
clone._content = null;
// turn off change tracking
clone.DisableChangeTracking();
clonedFile._content = null;
// ...
DeepCloneNameAndAlias(clone);
// this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
// re-enable tracking
clone.EnableChangeTracking();
return clone;
DeepCloneNameAndAlias(clonedFile);
}
}
}

View File

@@ -284,22 +284,18 @@ namespace Umbraco.Core.Models
get { return _properties; }
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (Macro)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
clone._addedProperties = new List<string>();
clone._removedProperties = new List<string>();
clone._properties = (MacroPropertyCollection)Properties.DeepClone();
//re-assign the event handler
clone._properties.CollectionChanged += clone.PropertiesChanged;
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
base.PerformDeepClone(clone);
return clone;
var clonedEntity = (Macro)clone;
clonedEntity._addedProperties = new List<string>();
clonedEntity._removedProperties = new List<string>();
clonedEntity._properties = (MacroPropertyCollection)Properties.DeepClone();
//re-assign the event handler
clonedEntity._properties.CollectionChanged += clonedEntity.PropertiesChanged;
}
}
}

View File

@@ -598,20 +598,15 @@ namespace Umbraco.Core.Models
return true;
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (Member)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
base.PerformDeepClone(clone);
var clonedEntity = (Member)clone;
//need to manually clone this since it's not settable
clone._contentType = (IMemberType)ContentType.DeepClone();
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
clonedEntity._contentType = (IMemberType)ContentType.DeepClone();
}
/// <inheritdoc />

View File

@@ -448,18 +448,19 @@ namespace Umbraco.Core.Models.Membership
[DoNotClone]
internal object AdditionalDataLock { get { return _additionalDataLock; } }
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (User)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
base.PerformDeepClone(clone);
var clonedEntity = (User)clone;
//manually clone the start node props
clone._startContentIds = _startContentIds.ToArray();
clone._startMediaIds = _startMediaIds.ToArray();
clonedEntity._startContentIds = _startContentIds.ToArray();
clonedEntity._startMediaIds = _startMediaIds.ToArray();
// this value has been cloned and points to the same object
// which obviously is bad - needs to point to a new object
clone._additionalDataLock = new object();
clonedEntity._additionalDataLock = new object();
if (_additionalData != null)
{
@@ -467,7 +468,7 @@ namespace Umbraco.Core.Models.Membership
// changing one clone impacts all of them - so we need to reset it with a fresh
// dictionary that will contain the same values - and, if some values are deep
// cloneable, they should be deep-cloned too
var cloneAdditionalData = clone._additionalData = new Dictionary<string, object>();
var cloneAdditionalData = clonedEntity._additionalData = new Dictionary<string, object>();
lock (_additionalDataLock)
{
@@ -480,15 +481,9 @@ namespace Umbraco.Core.Models.Membership
}
//need to create new collections otherwise they'll get copied by ref
clone._userGroups = new HashSet<IReadOnlyUserGroup>(_userGroups);
clone._allowedSections = _allowedSections != null ? new List<string>(_allowedSections) : null;
//re-create the event handler
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
clonedEntity._userGroups = new HashSet<IReadOnlyUserGroup>(_userGroups);
clonedEntity._allowedSections = _allowedSections != null ? new List<string>(_allowedSections) : null;
}
/// <summary>

View File

@@ -392,21 +392,14 @@ namespace Umbraco.Core.Models
return PropertyType.IsPropertyValueValid(value);
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (Property) base.DeepClone();
base.PerformDeepClone(clone);
//turn off change tracking
clone.DisableChangeTracking();
var clonedEntity = (Property)clone;
//need to manually assign since this is a readonly property
clone.PropertyType = (PropertyType) PropertyType.DeepClone();
//re-enable tracking
clone.ResetDirtyProperties(false); // not needed really, since we're not tracking
clone.EnableChangeTracking();
return clone;
clonedEntity.PropertyType = (PropertyType) PropertyType.DeepClone();
}
}
}

View File

@@ -100,24 +100,18 @@ namespace Umbraco.Core.Models
return baseHash ^ nameHash;
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (PropertyGroup)base.DeepClone();
base.PerformDeepClone(clone);
//turn off change tracking
clone.DisableChangeTracking();
var clonedEntity = (PropertyGroup)clone;
if (clone._propertyTypes != null)
if (clonedEntity._propertyTypes != null)
{
clone._propertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
clone._propertyTypes = (PropertyTypeCollection) _propertyTypes.DeepClone(); //manually deep clone
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged; //re-assign correct event handler
clonedEntity._propertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
clonedEntity._propertyTypes = (PropertyTypeCollection) _propertyTypes.DeepClone(); //manually deep clone
clonedEntity._propertyTypes.CollectionChanged += clonedEntity.PropertyTypesChanged; //re-assign correct event handler
}
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
}
}

View File

@@ -424,22 +424,17 @@ namespace Umbraco.Core.Models
}
/// <inheritdoc />
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (PropertyType)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
base.PerformDeepClone(clone);
var clonedEntity = (PropertyType)clone;
//need to manually assign the Lazy value as it will not be automatically mapped
if (PropertyGroupId != null)
{
clone._propertyGroupId = new Lazy<int>(() => PropertyGroupId.Value);
clonedEntity._propertyGroupId = new Lazy<int>(() => PropertyGroupId.Value);
}
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
}
}

View File

@@ -153,23 +153,17 @@ namespace Umbraco.Core.Models
}
}
public override object DeepClone()
protected override void PerformDeepClone(object clone)
{
var clone = (PublicAccessEntry) base.DeepClone();
base.PerformDeepClone(clone);
//turn off change tracking
clone.DisableChangeTracking();
var cloneEntity = (PublicAccessEntry)clone;
if (clone._ruleCollection != null)
if (cloneEntity._ruleCollection != null)
{
clone._ruleCollection.CollectionChanged -= _ruleCollection_CollectionChanged; //clear this event handler if any
clone._ruleCollection.CollectionChanged += clone._ruleCollection_CollectionChanged; //re-assign correct event handler
cloneEntity._ruleCollection.CollectionChanged -= _ruleCollection_CollectionChanged; //clear this event handler if any
cloneEntity._ruleCollection.CollectionChanged += cloneEntity._ruleCollection_CollectionChanged; //re-assign correct event handler
}
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
}
}