diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 59cd36b3f8..0f843f9565 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -336,26 +336,6 @@ namespace Umbraco.Core.Models
}
}
- /////
- ///// Creates a clone of the current entity
- /////
- /////
- //public IContent Clone()
- //{
- // var clone = (Content)this.MemberwiseClone();
- // clone.Key = Guid.Empty;
- // clone.Version = Guid.NewGuid();
- // clone.ResetIdentity();
-
- // foreach (var property in clone.Properties)
- // {
- // property.ResetIdentity();
- // property.Version = clone.Version;
- // }
-
- // return clone;
- //}
-
///
/// Indicates whether a specific property on the current entity is dirty.
///
@@ -437,13 +417,12 @@ namespace Umbraco.Core.Models
}
///
- /// TODO: Remove this as it's really only a shallow clone and not thread safe
- /// Creates a clone of the current entity
+ /// Creates a deep clone of the current entity with its identity and it's property identities reset
///
///
public IContent Clone()
{
- var clone = (Content)this.MemberwiseClone();
+ var clone = (Content)DeepClone();
clone.Key = Guid.Empty;
clone.Version = Guid.NewGuid();
clone.ResetIdentity();
@@ -457,17 +436,17 @@ namespace Umbraco.Core.Models
return clone;
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var asContent = (Content)(object)clone;
+ var asContent = (Content)clone;
if (Template != null)
{
- asContent.Template = Template.DeepClone();
+ asContent.Template = (ITemplate)Template.DeepClone();
}
- asContent._contentType = ContentType.DeepClone();
+ asContent._contentType = (IContentType)ContentType.DeepClone();
asContent.ResetDirtyProperties(true);
return clone;
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 521afeee78..75af29263f 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -448,6 +448,7 @@ namespace Umbraco.Core.Models
///
/// Returns a collection of the result of the last validation process, this collection contains all invalid properties.
///
+ [IgnoreDataMember]
internal IEnumerable LastInvalidProperties
{
get { return _lastInvalidProperties; }
@@ -470,13 +471,13 @@ namespace Umbraco.Core.Models
}
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
//cast to this object to set the complex properties
var asContentBase = (IContentBase)clone;
- asContentBase.Properties = Properties.DeepClone();
+ asContentBase.Properties = (PropertyCollection)Properties.DeepClone();
var tracksChanges = clone as TracksChangesEntityBase;
if (tracksChanges != null)
diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs
index 2d6f180ee4..12c528278b 100644
--- a/src/Umbraco.Core/Models/ContentType.cs
+++ b/src/Umbraco.Core/Models/ContentType.cs
@@ -175,12 +175,12 @@ namespace Umbraco.Core.Models
return clone;
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var contentType = (ContentType)(object)clone;
- contentType.AllowedTemplates = AllowedTemplates.Select(t => t.DeepClone()).ToArray();
+ var contentType = (ContentType)clone;
+ contentType.AllowedTemplates = AllowedTemplates.Select(t => (ITemplate)t.DeepClone()).ToArray();
contentType.ResetDirtyProperties(true);
return clone;
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index b13e1d4115..dfa34fc226 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -578,16 +578,16 @@ namespace Umbraco.Core.Models
}
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var contentType = (ContentTypeBase)(object)clone;
+ var contentType = (ContentTypeBase)clone;
contentType._additionalData = new Dictionary();
contentType._additionalData.MergeLeft(_additionalData);
- contentType.AllowedContentTypes = AllowedContentTypes.Select(x => x.DeepClone()).ToList();
- contentType.PropertyGroups = PropertyGroups.DeepClone();
- contentType.PropertyTypes = PropertyTypes.Select(x => x.DeepClone()).ToList();
+ contentType.AllowedContentTypes = AllowedContentTypes.Select(x => (ContentTypeSort)x.DeepClone()).ToList();
+ contentType.PropertyGroups = (PropertyGroupCollection)PropertyGroups.DeepClone();
+ contentType.PropertyTypes = PropertyTypes.Select(x => (PropertyType)x.DeepClone()).ToList();
contentType.ResetDirtyProperties(true);
diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
index 138f2cff45..b8b48e7eec 100644
--- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
@@ -218,13 +218,13 @@ namespace Umbraco.Core.Models
.Union(ContentTypeComposition.SelectMany(x => x.CompositionIds()));
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var contentType = (ContentTypeCompositionBase)(object)clone;
+ var contentType = (ContentTypeCompositionBase)clone;
contentType.RemovedContentTypeKeyTracker = new List();
- contentType._contentTypeComposition = ContentTypeComposition.Select(x => x.DeepClone()).ToList();
+ contentType._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
contentType.ResetDirtyProperties(true);
return clone;
diff --git a/src/Umbraco.Core/Models/ContentTypeSort.cs b/src/Umbraco.Core/Models/ContentTypeSort.cs
index 431b1e2cbe..a52b5ae0f1 100644
--- a/src/Umbraco.Core/Models/ContentTypeSort.cs
+++ b/src/Umbraco.Core/Models/ContentTypeSort.cs
@@ -35,12 +35,12 @@ namespace Umbraco.Core.Models
public string Alias { get; set; }
- public T DeepClone() where T: IDeepCloneable
+ public object DeepClone()
{
var clone = (ContentTypeSort)MemberwiseClone();
var id = Id.Value;
clone.Id = new Lazy(() => id);
- return (T)(IDeepCloneable)clone;
+ return clone;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs
index 3d8d7b40b3..27dad979fc 100644
--- a/src/Umbraco.Core/Models/EntityBase/Entity.cs
+++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs
@@ -96,7 +96,7 @@ namespace Umbraco.Core.Models.EntityBase
}
///
- /// /// Gets or sets the WasCancelled flag, which is used to track
+ /// Gets or sets the WasCancelled flag, which is used to track
/// whether some action against an entity was cancelled through some event.
/// This only exists so we have a way to check if an event was cancelled through
/// the new api, which also needs to take effect in the legacy api.
@@ -158,6 +158,7 @@ namespace Umbraco.Core.Models.EntityBase
///
/// Indicates whether the current entity has an identity, eg. Id.
///
+ [DataMember]
public virtual bool HasIdentity
{
get
@@ -230,13 +231,13 @@ namespace Umbraco.Core.Models.EntityBase
return _hash.Value;
}
- public virtual T DeepClone() where T : IDeepCloneable
+ public virtual object DeepClone()
{
//Memberwise clone on Entity will work since it doesn't have any deep elements
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
var clone = MemberwiseClone();
((TracksChangesEntityBase)clone).ResetDirtyProperties(true);
- return (T)clone;
+ return clone;
//Using data contract serializer - has issues
diff --git a/src/Umbraco.Core/Models/EntityBase/IDeepCloneable.cs b/src/Umbraco.Core/Models/EntityBase/IDeepCloneable.cs
index 9d64e22858..62d8ba7f77 100644
--- a/src/Umbraco.Core/Models/EntityBase/IDeepCloneable.cs
+++ b/src/Umbraco.Core/Models/EntityBase/IDeepCloneable.cs
@@ -2,6 +2,6 @@ namespace Umbraco.Core.Models.EntityBase
{
public interface IDeepCloneable
{
- T DeepClone() where T : IDeepCloneable;
+ object DeepClone();
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs
index ff5dac1a96..49f2557d40 100644
--- a/src/Umbraco.Core/Models/File.cs
+++ b/src/Umbraco.Core/Models/File.cs
@@ -105,11 +105,11 @@ namespace Umbraco.Core.Models
/// True if file is valid, otherwise false
public abstract bool IsValid();
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var asFile = (File)(object)clone;
+ var asFile = (File)clone;
asFile._alias = Alias;
asFile._name = Name;
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index 45c2a8e617..04aa545612 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -143,12 +143,12 @@ namespace Umbraco.Core.Models
return _propertyType.IsPropertyValueValid(value);
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var asProperty = (Property)(object)clone;
- asProperty._propertyType = PropertyType.DeepClone();
+ var asProperty = (Property)clone;
+ asProperty._propertyType = (PropertyType)PropertyType.DeepClone();
asProperty.ResetDirtyProperties(true);
var tracksChanges = clone as TracksChangesEntityBase;
diff --git a/src/Umbraco.Core/Models/PropertyCollection.cs b/src/Umbraco.Core/Models/PropertyCollection.cs
index 79ae50bc05..0ff2e13484 100644
--- a/src/Umbraco.Core/Models/PropertyCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyCollection.cs
@@ -201,14 +201,14 @@ namespace Umbraco.Core.Models
/// Create a deep clone of this property collection
///
///
- public T DeepClone() where T: IDeepCloneable
+ public object DeepClone()
{
var newList = new PropertyCollection();
foreach (var p in this)
{
- newList.Add(p.DeepClone());
+ newList.Add((Property)p.DeepClone());
}
- return (T)(object)newList;
+ return newList;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs
index 73125f576c..000ae3959a 100644
--- a/src/Umbraco.Core/Models/PropertyGroup.cs
+++ b/src/Umbraco.Core/Models/PropertyGroup.cs
@@ -159,12 +159,12 @@ namespace Umbraco.Core.Models
return clone;
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var propertyGroup = (PropertyGroup)(object)clone;
- propertyGroup.PropertyTypes = PropertyTypes.DeepClone();
+ var propertyGroup = (PropertyGroup)clone;
+ propertyGroup.PropertyTypes = (PropertyTypeCollection)PropertyTypes.DeepClone();
propertyGroup.ResetDirtyProperties(true);
return clone;
diff --git a/src/Umbraco.Core/Models/PropertyGroupCollection.cs b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
index 57fad535f1..6e1847856b 100644
--- a/src/Umbraco.Core/Models/PropertyGroupCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyGroupCollection.cs
@@ -134,14 +134,14 @@ namespace Umbraco.Core.Models
}
}
- public T DeepClone() where T : IDeepCloneable
+ public object DeepClone()
{
var newGroup = new PropertyGroupCollection();
foreach (var p in this)
{
- newGroup.Add(p.DeepClone());
+ newGroup.Add((PropertyGroup)p.DeepClone());
}
- return (T)(object)newGroup;
+ return newGroup;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index 0c6b169c2b..28014ed2d6 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -419,11 +419,11 @@ namespace Umbraco.Core.Models
return clone;
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var asPropertyType = (PropertyType)(object)clone;
+ var asPropertyType = (PropertyType)clone;
if (PropertyGroupId != null)
{
diff --git a/src/Umbraco.Core/Models/PropertyTypeCollection.cs b/src/Umbraco.Core/Models/PropertyTypeCollection.cs
index 34588f2e80..ea153ccae4 100644
--- a/src/Umbraco.Core/Models/PropertyTypeCollection.cs
+++ b/src/Umbraco.Core/Models/PropertyTypeCollection.cs
@@ -136,14 +136,14 @@ namespace Umbraco.Core.Models
}
}
- public T DeepClone() where T : IDeepCloneable
+ public object DeepClone()
{
var newGroup = new PropertyTypeCollection();
foreach (var p in this)
{
- newGroup.Add(p.DeepClone());
+ newGroup.Add((PropertyType)p.DeepClone());
}
- return (T)(object)newGroup;
+ return newGroup;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Template.cs b/src/Umbraco.Core/Models/Template.cs
index 5a152bd92b..808e4627b7 100644
--- a/src/Umbraco.Core/Models/Template.cs
+++ b/src/Umbraco.Core/Models/Template.cs
@@ -197,11 +197,11 @@ namespace Umbraco.Core.Models
MasterTemplateId = new Lazy(() => masterTemplate.Id);
}
- public override T DeepClone()
+ public override object DeepClone()
{
- var clone = base.DeepClone();
+ var clone = base.DeepClone();
- var asTemplate = (Template)(object)clone;
+ var asTemplate = (Template)clone;
asTemplate._alias = Alias;
asTemplate._name = Name;
diff --git a/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs
index 018ef43ff6..928511e407 100644
--- a/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs
+++ b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs
@@ -420,9 +420,9 @@ namespace Umbraco.Tests.Models.Collections
return _hash.Value;
}*/
- public T DeepClone() where T : IDeepCloneable
+ public object DeepClone()
{
- return (T)this.MemberwiseClone();
+ return this.MemberwiseClone();
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs
index f35818eb7e..f676569c5f 100644
--- a/src/Umbraco.Tests/Models/ContentTests.cs
+++ b/src/Umbraco.Tests/Models/ContentTests.cs
@@ -6,6 +6,7 @@ using System.Web;
using Moq;
using NUnit.Framework;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
@@ -111,7 +112,7 @@ namespace Umbraco.Tests.Models
[Test]
- public void Can_Clone_Content()
+ public void Can_Clone_Content_With_Reset_Identity()
{
// Arrange
var contentType = MockedContentTypes.CreateTextpageContentType();
@@ -120,7 +121,7 @@ namespace Umbraco.Tests.Models
content.Key = new Guid("29181B97-CB8F-403F-86DE-5FEB497F4800");
// Act
- var clone = content.DeepClone();
+ var clone = content.Clone();
// Assert
Assert.AreNotSame(clone, content);
@@ -131,6 +132,99 @@ namespace Umbraco.Tests.Models
Assert.AreNotSame(content.Properties, clone.Properties);
}
+ [Test]
+ public void Can_Deep_Clone()
+ {
+ // Arrange
+ var contentType = MockedContentTypes.CreateTextpageContentType();
+ contentType.Id = 99;
+ var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
+ var i = 200;
+ foreach (var property in content.Properties)
+ {
+ property.Id = ++i;
+ }
+ content.Id = 10;
+ content.CreateDate = DateTime.Now;
+ content.CreatorId = 22;
+ content.ExpireDate = DateTime.Now;
+ content.Key = Guid.NewGuid();
+ content.Language = "en";
+ content.Level = 3;
+ content.Path = "-1,4,10";
+ content.ReleaseDate = DateTime.Now;
+ content.ChangePublishedState(PublishedState.Published);
+ content.SortOrder = 5;
+ content.Template = new Template("-1,2,3,4", "Test Template", "testTemplate")
+ {
+ Id = 88
+ };
+ content.Trashed = false;
+ content.UpdateDate = DateTime.Now;
+ content.Version = Guid.NewGuid();
+ content.WriterId = 23;
+
+ ((IUmbracoEntity)content).AdditionalData.Add("test1", 123);
+ ((IUmbracoEntity)content).AdditionalData.Add("test2", "hello");
+
+ // Act
+ var clone = (Content)content.DeepClone();
+
+ // Assert
+ Assert.AreNotSame(clone, content);
+ Assert.AreEqual(clone, content);
+ Assert.AreEqual(clone.Id, content.Id);
+ Assert.AreEqual(clone.Version, content.Version);
+ Assert.AreEqual(((IUmbracoEntity)clone).AdditionalData, ((IUmbracoEntity)content).AdditionalData);
+ Assert.AreNotSame(clone.ContentType, content.ContentType);
+ Assert.AreEqual(clone.ContentType, content.ContentType);
+ Assert.AreEqual(clone.ContentType.PropertyGroups.Count, content.ContentType.PropertyGroups.Count);
+ for (var index = 0; index < content.ContentType.PropertyGroups.Count; index++)
+ {
+ Assert.AreNotSame(clone.ContentType.PropertyGroups[index], content.ContentType.PropertyGroups[index]);
+ Assert.AreEqual(clone.ContentType.PropertyGroups[index], content.ContentType.PropertyGroups[index]);
+ }
+ Assert.AreEqual(clone.ContentType.PropertyTypes.Count(), content.ContentType.PropertyTypes.Count());
+ for (var index = 0; index < content.ContentType.PropertyTypes.Count(); index++)
+ {
+ Assert.AreNotSame(clone.ContentType.PropertyTypes.ElementAt(index), content.ContentType.PropertyTypes.ElementAt(index));
+ Assert.AreEqual(clone.ContentType.PropertyTypes.ElementAt(index), content.ContentType.PropertyTypes.ElementAt(index));
+ }
+ Assert.AreEqual(clone.ContentTypeId, content.ContentTypeId);
+ Assert.AreEqual(clone.CreateDate, content.CreateDate);
+ Assert.AreEqual(clone.CreatorId, content.CreatorId);
+ Assert.AreEqual(clone.ExpireDate, content.ExpireDate);
+ Assert.AreEqual(clone.Key, content.Key);
+ Assert.AreEqual(clone.Language, content.Language);
+ Assert.AreEqual(clone.Level, content.Level);
+ Assert.AreEqual(clone.Path, content.Path);
+ Assert.AreEqual(clone.ReleaseDate, content.ReleaseDate);
+ Assert.AreEqual(clone.Published, content.Published);
+ Assert.AreEqual(clone.PublishedState, content.PublishedState);
+ Assert.AreEqual(clone.SortOrder, content.SortOrder);
+ Assert.AreEqual(clone.PublishedState, content.PublishedState);
+ Assert.AreNotSame(clone.Template, content.Template);
+ Assert.AreEqual(clone.Template, content.Template);
+ Assert.AreEqual(clone.Trashed, content.Trashed);
+ Assert.AreEqual(clone.UpdateDate, content.UpdateDate);
+ Assert.AreEqual(clone.Version, content.Version);
+ Assert.AreEqual(clone.WriterId, content.WriterId);
+ Assert.AreNotSame(clone.Properties, content.Properties);
+ Assert.AreEqual(clone.Properties.Count(), content.Properties.Count());
+ for (var index = 0; index < content.Properties.Count; index++)
+ {
+ Assert.AreNotSame(clone.Properties[index], content.Properties[index]);
+ Assert.AreEqual(clone.Properties[index], content.Properties[index]);
+ }
+
+ //This double verifies by reflection
+ var allProps = clone.GetType().GetProperties();
+ foreach (var propertyInfo in allProps)
+ {
+ Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(content, null));
+ }
+ }
+
/*[Test]
public void Cannot_Change_Property_With_Invalid_Value()
{