diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs
index 1cf7fc9684..ec763d07fa 100644
--- a/src/Umbraco.Core/Models/ContentType.cs
+++ b/src/Umbraco.Core/Models/ContentType.cs
@@ -133,29 +133,6 @@ namespace Umbraco.Core.Models
Key = Guid.NewGuid();
}
- ///
- /// Method to call when Entity is being updated
- ///
- /// Modified Date is set and a new Version guid is set
- internal override void UpdatingEntity()
- {
- base.UpdatingEntity();
- }
-
- public override object DeepClone()
- {
- var clone = (ContentType)base.DeepClone();
- var propertyGroups = PropertyGroups.Select(x => (PropertyGroup)x.DeepClone()).ToList();
- clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
- //set the property types that are not part of a group
- clone.PropertyTypes = PropertyTypeCollection
- .Where(x => x.PropertyGroupId == null)
- .Select(x => (PropertyType)x.DeepClone()).ToList();
-
- clone.ResetDirtyProperties(false);
-
- return clone;
- }
///
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index da4cdcfd19..5c09deadb8 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -351,7 +351,9 @@ namespace Umbraco.Core.Models
///
/// List of PropertyGroups available on this ContentType
///
- /// A PropertyGroup corresponds to a Tab in the UI
+ ///
+ /// A PropertyGroup corresponds to a Tab in the UI
+ ///
[DataMember]
public virtual PropertyGroupCollection PropertyGroups
{
@@ -367,7 +369,13 @@ namespace Umbraco.Core.Models
/// List of PropertyTypes available on this ContentType.
/// This list aggregates PropertyTypes across the PropertyGroups.
///
+ ///
+ /// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
+ /// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
+ /// data. We manually clone this instead.
+ ///
[IgnoreDataMember]
+ [DoNotClone]
public virtual IEnumerable PropertyTypes
{
get
@@ -383,6 +391,14 @@ namespace Umbraco.Core.Models
}
///
+ /// Returns the property type collection containing types that are non-groups - used for tests
+ ///
+ internal IEnumerable NonGroupedPropertyTypes
+ {
+ get { return _propertyTypes; }
+ }
+
+ ///
/// A boolean flag indicating if a property type has been removed from this instance.
///
///
@@ -579,5 +595,19 @@ namespace Umbraco.Core.Models
propertyType.ResetDirtyProperties();
}
}
+
+ public override object DeepClone()
+ {
+ var clone = (ContentTypeBase)base.DeepClone();
+
+ //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._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
+ clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
+
+ return clone;
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/DeepCloneHelper.cs b/src/Umbraco.Core/Models/DeepCloneHelper.cs
index aaee03f963..d8ef10751b 100644
--- a/src/Umbraco.Core/Models/DeepCloneHelper.cs
+++ b/src/Umbraco.Core/Models/DeepCloneHelper.cs
@@ -2,9 +2,29 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
namespace Umbraco.Core.Models
{
+ ///
+ /// Used to attribute properties that have a setter and are a reference type
+ /// that should be ignored for cloning when using the DeepCloneHelper
+ ///
+ ///
+ ///
+ /// This attribute must be used:
+ /// * when the property is backed by a field but the result of the property is the un-natural data stored in the field
+ ///
+ /// This attribute should not be used:
+ /// * when the property is virtual
+ /// * when the setter performs additional required logic other than just setting the underlying field
+ ///
+ ///
+ internal class DoNotCloneAttribute : Attribute
+ {
+
+ }
+
public static class DeepCloneHelper
{
///
@@ -25,8 +45,10 @@ namespace Umbraco.Core.Models
var refProperties = inputType.GetProperties()
.Where(x =>
+ //is not attributed with the ignore clone attribute
+ x.GetCustomAttribute() == null
//reference type but not string
- x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
+ && x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
//settable
&& x.CanWrite
//non-indexed
diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
index c2ca53a240..acacadb0cc 100644
--- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
+++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
@@ -128,9 +128,9 @@ namespace Umbraco.Core.Persistence.Caching
public void Save(Type type, IEntity entity)
{
//IMPORTANT: we must clone to store, see: http://issues.umbraco.org/issue/U4-4259
- entity = (IEntity)entity.DeepClone();
+ var clone = (IEntity)entity.DeepClone();
- var key = GetCompositeId(type, entity.Id);
+ var key = GetCompositeId(type, clone.Id);
_keyTracker.TryAdd(key);
@@ -139,11 +139,11 @@ namespace Umbraco.Core.Persistence.Caching
if (_memoryCache != null)
{
- _memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
+ _memoryCache.Set(key, clone, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
}
else
{
- HttpRuntime.Cache.Insert(key, entity, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
+ HttpRuntime.Cache.Insert(key, clone, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
}
}
diff --git a/src/Umbraco.Tests/Models/ContentTypeTests.cs b/src/Umbraco.Tests/Models/ContentTypeTests.cs
index dc2de0d6bb..43b90c424e 100644
--- a/src/Umbraco.Tests/Models/ContentTypeTests.cs
+++ b/src/Umbraco.Tests/Models/ContentTypeTests.cs
@@ -162,6 +162,7 @@ namespace Umbraco.Tests.Models
}
Assert.AreNotSame(clone.PropertyTypes, contentType.PropertyTypes);
Assert.AreEqual(clone.PropertyTypes.Count(), contentType.PropertyTypes.Count());
+ Assert.AreEqual(0, ((ContentTypeBase)clone).NonGroupedPropertyTypes.Count());
for (var index = 0; index < contentType.PropertyTypes.Count(); index++)
{
Assert.AreNotSame(clone.PropertyTypes.ElementAt(index), contentType.PropertyTypes.ElementAt(index));