diff --git a/src/Umbraco.Core/Models/ContentTypeSort.cs b/src/Umbraco.Core/Models/ContentTypeSort.cs index a52b5ae0f1..5aa81d9db0 100644 --- a/src/Umbraco.Core/Models/ContentTypeSort.cs +++ b/src/Umbraco.Core/Models/ContentTypeSort.cs @@ -42,5 +42,27 @@ namespace Umbraco.Core.Models clone.Id = new Lazy(() => id); return clone; } + + protected bool Equals(ContentTypeSort other) + { + return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ContentTypeSort) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (Id.GetHashCode()*397) ^ Alias.GetHashCode(); + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs index c670a75108..b29e618f83 100644 --- a/src/Umbraco.Core/Models/DictionaryItem.cs +++ b/src/Umbraco.Core/Models/DictionaryItem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; @@ -96,5 +97,12 @@ namespace Umbraco.Core.Models if(ParentId == Guid.Empty) _parentId = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde"); } + + public override object DeepClone() + { + var clone = (DictionaryItem)base.DeepClone(); + clone.Translations = Translations.Select(x => (IDictionaryTranslation) x.DeepClone()).ToList(); + return clone; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/DictionaryTranslation.cs b/src/Umbraco.Core/Models/DictionaryTranslation.cs index 5f8f5ebc25..b815ab8c5b 100644 --- a/src/Umbraco.Core/Models/DictionaryTranslation.cs +++ b/src/Umbraco.Core/Models/DictionaryTranslation.cs @@ -65,5 +65,12 @@ namespace Umbraco.Core.Models }, _value, ValueSelector); } } + + public override object DeepClone() + { + var clone = (DictionaryTranslation)base.DeepClone(); + clone.Language = (ILanguage)Language.DeepClone(); + return clone; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Relation.cs b/src/Umbraco.Core/Models/Relation.cs index ec7c3741a6..2eef36cf73 100644 --- a/src/Umbraco.Core/Models/Relation.cs +++ b/src/Umbraco.Core/Models/Relation.cs @@ -107,5 +107,12 @@ namespace Umbraco.Core.Models { get { return _relationType.Id; } } + + public override object DeepClone() + { + var clone = (Relation)base.DeepClone(); + clone.RelationType = (RelationType)RelationType.DeepClone(); + return clone; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/RelationType.cs b/src/Umbraco.Core/Models/RelationType.cs index e1f5b4a388..dfe84659de 100644 --- a/src/Umbraco.Core/Models/RelationType.cs +++ b/src/Umbraco.Core/Models/RelationType.cs @@ -118,5 +118,6 @@ namespace Umbraco.Core.Models }, _childObjectType, ChildObjectTypeSelector); } } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ServerRegistration.cs b/src/Umbraco.Core/Models/ServerRegistration.cs index dd352c2c86..7f43f5dfd2 100644 --- a/src/Umbraco.Core/Models/ServerRegistration.cs +++ b/src/Umbraco.Core/Models/ServerRegistration.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Reflection; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Mappers; @@ -34,7 +35,7 @@ namespace Umbraco.Core.Models { UpdateDate = updateDate; CreateDate = createDate; - Key = Id.ToString().EncodeAsGuid(); + Key = Id.ToString(CultureInfo.InvariantCulture).EncodeAsGuid(); Id = id; ServerAddress = serverAddress; ComputerName = computerName; @@ -51,7 +52,7 @@ namespace Umbraco.Core.Models { CreateDate = createDate; UpdateDate = createDate; - Key = 0.ToString().EncodeAsGuid(); + Key = 0.ToString(CultureInfo.InvariantCulture).EncodeAsGuid(); ServerAddress = serverAddress; ComputerName = computerName; } diff --git a/src/Umbraco.Core/Models/Task.cs b/src/Umbraco.Core/Models/Task.cs index 86370c00bb..9a4b9eaa71 100644 --- a/src/Umbraco.Core/Models/Task.cs +++ b/src/Umbraco.Core/Models/Task.cs @@ -132,5 +132,12 @@ namespace Umbraco.Core.Models }, _comment, CommentSelector); } } + + public override object DeepClone() + { + var clone = (Task) base.DeepClone(); + clone.TaskType = (TaskType)TaskType.DeepClone(); + return clone; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs index 6822582576..347f6be5c7 100644 --- a/src/Umbraco.Core/Models/UmbracoEntity.cs +++ b/src/Umbraco.Core/Models/UmbracoEntity.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Reflection; using Umbraco.Core.Models.EntityBase; @@ -46,17 +47,20 @@ namespace Umbraco.Core.Models public UmbracoEntity() { AdditionalData = new Dictionary(); + UmbracoProperties = new List(); } public UmbracoEntity(bool trashed) { AdditionalData = new Dictionary(); + UmbracoProperties = new List(); Trashed = trashed; } public UmbracoEntity(int trashed) { AdditionalData = new Dictionary(); + UmbracoProperties = new List(); Trashed = trashed == 1; } @@ -287,10 +291,45 @@ namespace Umbraco.Core.Models /// public IList UmbracoProperties { get; set; } - internal class UmbracoProperty + public override object DeepClone() + { + var clone = (UmbracoEntity)base.DeepClone(); + clone.UmbracoProperties = UmbracoProperties.Select(x => (UmbracoProperty) x.DeepClone()).ToList(); + return clone; + } + + internal class UmbracoProperty : IDeepCloneable { public Guid DataTypeControlId { get; set; } public string Value { get; set; } + public 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(); + return clone; + } + + protected bool Equals(UmbracoProperty other) + { + return DataTypeControlId.Equals(other.DataTypeControlId) && string.Equals(Value, other.Value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((UmbracoProperty) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (DataTypeControlId.GetHashCode()*397) ^ (Value != null ? Value.GetHashCode() : 0); + } + } } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/Collections/Item.cs b/src/Umbraco.Tests/Models/Collections/Item.cs new file mode 100644 index 0000000000..ff1789a850 --- /dev/null +++ b/src/Umbraco.Tests/Models/Collections/Item.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using Umbraco.Core; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Tests.Models.Collections +{ + public abstract class Item : IEntity, ICanBeDirty + { + private bool _hasIdentity; + private int? _hash; + private int _id; + private Guid _key; + + protected Item() + { + _propertyChangedInfo = new Dictionary(); + } + + /// + /// Integer Id + /// + [DataMember] + public int Id + { + get + { + return _id; + } + set + { + _id = value; + HasIdentity = true; + } + } + + /// + /// Guid based Id + /// + /// The key is currectly used to store the Unique Id from the + /// umbracoNode table, which many of the entities are based on. + [DataMember] + public Guid Key + { + get + { + if (_key == Guid.Empty) + return _id.ToGuid(); + + return _key; + } + set { _key = value; } + } + + /// + /// Gets or sets the Created Date + /// + [DataMember] + public DateTime CreateDate { get; set; } + + /// + /// Gets or sets the Modified Date + /// + [DataMember] + public DateTime UpdateDate { get; set; } + + /// + /// 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. + /// + [IgnoreDataMember] + internal bool WasCancelled { get; set; } + + /// + /// Property changed event + /// + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Method to call on a property setter. + /// + /// The property info. + protected virtual void OnPropertyChanged(PropertyInfo propertyInfo) + { + _propertyChangedInfo[propertyInfo.Name] = true; + + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(propertyInfo.Name)); + } + } + + internal virtual void ResetIdentity() + { + _hasIdentity = false; + _id = default(int); + } + + /// + /// Method to call on entity saved when first added + /// + internal virtual void AddingEntity() + { + CreateDate = DateTime.Now; + UpdateDate = DateTime.Now; + } + + /// + /// Method to call on entity saved/updated + /// + internal virtual void UpdatingEntity() + { + UpdateDate = DateTime.Now; + } + + /// + /// Tracks the properties that have changed + /// + //private readonly IDictionary _propertyChangedInfo = new Dictionary(); + private IDictionary _propertyChangedInfo; + + /// + /// Indicates whether a specific property on the current entity is dirty. + /// + /// Name of the property to check + /// True if Property is dirty, otherwise False + public virtual bool IsPropertyDirty(string propertyName) + { + return _propertyChangedInfo.Any(x => x.Key == propertyName); + } + + /// + /// Indicates whether the current entity is dirty. + /// + /// True if entity is dirty, otherwise False + public virtual bool IsDirty() + { + return _propertyChangedInfo.Any(); + } + + /// + /// Resets dirty properties by clearing the dictionary used to track changes. + /// + /// + /// Please note that resetting the dirty properties could potentially + /// obstruct the saving of a new or updated entity. + /// + public virtual void ResetDirtyProperties() + { + _propertyChangedInfo.Clear(); + } + + /// + /// Indicates whether the current entity has an identity, eg. Id. + /// + public virtual bool HasIdentity + { + get + { + return _hasIdentity; + } + protected set + { + _hasIdentity = value; + } + } + + public static bool operator ==(Item left, Item right) + { + /*if (ReferenceEquals(null, left)) + return false; + + if(ReferenceEquals(null, right)) + return false;*/ + + return ReferenceEquals(left, right); + + return left.Equals(right); + } + + public static bool operator !=(Item left, Item right) + { + return !(left == right); + } + + /*public virtual bool SameIdentityAs(IEntity other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + + return SameIdentityAs(other as Entity); + } + + public virtual bool Equals(Entity other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + + return SameIdentityAs(other); + } + + public virtual Type GetRealType() + { + return GetType(); + } + + public virtual bool SameIdentityAs(Entity other) + { + if (ReferenceEquals(null, other)) + return false; + + if (ReferenceEquals(this, other)) + return true; + + if (GetType() == other.GetRealType() && HasIdentity && other.HasIdentity) + return other.Id.Equals(Id); + + return false; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + + return SameIdentityAs(obj as IEntity); + } + + public override int GetHashCode() + { + if (!_hash.HasValue) + _hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode()); + return _hash.Value; + }*/ + + public object DeepClone() + { + return this.MemberwiseClone(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/Collections/OrderItem.cs b/src/Umbraco.Tests/Models/Collections/OrderItem.cs new file mode 100644 index 0000000000..81c770db92 --- /dev/null +++ b/src/Umbraco.Tests/Models/Collections/OrderItem.cs @@ -0,0 +1,42 @@ +using System; + +namespace Umbraco.Tests.Models.Collections +{ + public class OrderItem : Item + { + public readonly int PartNumber; + public readonly string Description; + public readonly double UnitPrice; + + private int _quantity = 0; + + public OrderItem(int partNumber, string description, + int quantity, double unitPrice) + { + this.PartNumber = partNumber; + this.Description = description; + this.Quantity = quantity; + this.UnitPrice = unitPrice; + } + + public int Quantity + { + get { return _quantity; } + set + { + if (value < 0) + throw new ArgumentException("Quantity cannot be negative."); + + _quantity = value; + } + } + + public override string ToString() + { + return String.Format( + "{0,9} {1,6} {2,-12} at {3,8:#,###.00} = {4,10:###,###.00}", + PartNumber, _quantity, Description, UnitPrice, + UnitPrice * _quantity); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs index 928511e407..c0ab5b7f7d 100644 --- a/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs +++ b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs @@ -1,15 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; +using System.Collections.Generic; using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Models.Collections @@ -71,358 +64,4 @@ namespace Umbraco.Tests.Models.Collections Assert.That(contentType.PropertyGroups.Any(x => x.Name.InvariantEquals("Test")), Is.False); } } - - public class SimpleOrder : KeyedCollection, INotifyCollectionChanged - { - // The parameterless constructor of the base class creates a - // KeyedCollection with an internal dictionary. For this code - // example, no other constructors are exposed. - // - public SimpleOrder() : base() { } - - public SimpleOrder(IEnumerable properties) - { - Reset(properties); - } - - // This is the only method that absolutely must be overridden, - // because without it the KeyedCollection cannot extract the - // keys from the items. The input parameter type is the - // second generic type argument, in this case OrderItem, and - // the return value type is the first generic type argument, - // in this case int. - // - protected override int GetKeyForItem(OrderItem item) - { - // In this example, the key is the part number. - return item.PartNumber; - } - - internal void Reset(IEnumerable properties) - { - Clear(); - properties.ForEach(Add); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - protected override void SetItem(int index, OrderItem item) - { - base.SetItem(index, item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); - } - - protected override void RemoveItem(int index) - { - var removed = this[index]; - base.RemoveItem(index); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed)); - } - - protected override void InsertItem(int index, OrderItem item) - { - base.InsertItem(index, item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); - } - - protected override void ClearItems() - { - base.ClearItems(); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - public new bool Contains(int partNumber) - { - return this.Any(x => x.PartNumber == partNumber); - } - - public event NotifyCollectionChangedEventHandler CollectionChanged; - - protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args) - { - if (CollectionChanged != null) - { - CollectionChanged(this, args); - } - } - } - - public class OrderItem : Item - { - public readonly int PartNumber; - public readonly string Description; - public readonly double UnitPrice; - - private int _quantity = 0; - - public OrderItem(int partNumber, string description, - int quantity, double unitPrice) - { - this.PartNumber = partNumber; - this.Description = description; - this.Quantity = quantity; - this.UnitPrice = unitPrice; - } - - public int Quantity - { - get { return _quantity; } - set - { - if (value < 0) - throw new ArgumentException("Quantity cannot be negative."); - - _quantity = value; - } - } - - public override string ToString() - { - return String.Format( - "{0,9} {1,6} {2,-12} at {3,8:#,###.00} = {4,10:###,###.00}", - PartNumber, _quantity, Description, UnitPrice, - UnitPrice * _quantity); - } - } - - public abstract class Item : IEntity, ICanBeDirty - { - private bool _hasIdentity; - private int? _hash; - private int _id; - private Guid _key; - - protected Item() - { - _propertyChangedInfo = new Dictionary(); - } - - /// - /// Integer Id - /// - [DataMember] - public int Id - { - get - { - return _id; - } - set - { - _id = value; - HasIdentity = true; - } - } - - /// - /// Guid based Id - /// - /// The key is currectly used to store the Unique Id from the - /// umbracoNode table, which many of the entities are based on. - [DataMember] - public Guid Key - { - get - { - if (_key == Guid.Empty) - return _id.ToGuid(); - - return _key; - } - set { _key = value; } - } - - /// - /// Gets or sets the Created Date - /// - [DataMember] - public DateTime CreateDate { get; set; } - - /// - /// Gets or sets the Modified Date - /// - [DataMember] - public DateTime UpdateDate { get; set; } - - /// - /// 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. - /// - [IgnoreDataMember] - internal bool WasCancelled { get; set; } - - /// - /// Property changed event - /// - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Method to call on a property setter. - /// - /// The property info. - protected virtual void OnPropertyChanged(PropertyInfo propertyInfo) - { - _propertyChangedInfo[propertyInfo.Name] = true; - - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propertyInfo.Name)); - } - } - - internal virtual void ResetIdentity() - { - _hasIdentity = false; - _id = default(int); - } - - /// - /// Method to call on entity saved when first added - /// - internal virtual void AddingEntity() - { - CreateDate = DateTime.Now; - UpdateDate = DateTime.Now; - } - - /// - /// Method to call on entity saved/updated - /// - internal virtual void UpdatingEntity() - { - UpdateDate = DateTime.Now; - } - - /// - /// Tracks the properties that have changed - /// - //private readonly IDictionary _propertyChangedInfo = new Dictionary(); - private IDictionary _propertyChangedInfo; - - /// - /// Indicates whether a specific property on the current entity is dirty. - /// - /// Name of the property to check - /// True if Property is dirty, otherwise False - public virtual bool IsPropertyDirty(string propertyName) - { - return _propertyChangedInfo.Any(x => x.Key == propertyName); - } - - /// - /// Indicates whether the current entity is dirty. - /// - /// True if entity is dirty, otherwise False - public virtual bool IsDirty() - { - return _propertyChangedInfo.Any(); - } - - /// - /// Resets dirty properties by clearing the dictionary used to track changes. - /// - /// - /// Please note that resetting the dirty properties could potentially - /// obstruct the saving of a new or updated entity. - /// - public virtual void ResetDirtyProperties() - { - _propertyChangedInfo.Clear(); - } - - /// - /// Indicates whether the current entity has an identity, eg. Id. - /// - public virtual bool HasIdentity - { - get - { - return _hasIdentity; - } - protected set - { - _hasIdentity = value; - } - } - - public static bool operator ==(Item left, Item right) - { - /*if (ReferenceEquals(null, left)) - return false; - - if(ReferenceEquals(null, right)) - return false;*/ - - return ReferenceEquals(left, right); - - return left.Equals(right); - } - - public static bool operator !=(Item left, Item right) - { - return !(left == right); - } - - /*public virtual bool SameIdentityAs(IEntity other) - { - if (ReferenceEquals(null, other)) - return false; - if (ReferenceEquals(this, other)) - return true; - - return SameIdentityAs(other as Entity); - } - - public virtual bool Equals(Entity other) - { - if (ReferenceEquals(null, other)) - return false; - if (ReferenceEquals(this, other)) - return true; - - return SameIdentityAs(other); - } - - public virtual Type GetRealType() - { - return GetType(); - } - - public virtual bool SameIdentityAs(Entity other) - { - if (ReferenceEquals(null, other)) - return false; - - if (ReferenceEquals(this, other)) - return true; - - if (GetType() == other.GetRealType() && HasIdentity && other.HasIdentity) - return other.Id.Equals(Id); - - return false; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - return false; - if (ReferenceEquals(this, obj)) - return true; - - return SameIdentityAs(obj as IEntity); - } - - public override int GetHashCode() - { - if (!_hash.HasValue) - _hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode()); - return _hash.Value; - }*/ - - public object DeepClone() - { - return this.MemberwiseClone(); - } - } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/Collections/SimpleOrder.cs b/src/Umbraco.Tests/Models/Collections/SimpleOrder.cs new file mode 100644 index 0000000000..82df104b64 --- /dev/null +++ b/src/Umbraco.Tests/Models/Collections/SimpleOrder.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using Umbraco.Core; + +namespace Umbraco.Tests.Models.Collections +{ + public class SimpleOrder : KeyedCollection, INotifyCollectionChanged + { + // The parameterless constructor of the base class creates a + // KeyedCollection with an internal dictionary. For this code + // example, no other constructors are exposed. + // + public SimpleOrder() : base() { } + + public SimpleOrder(IEnumerable properties) + { + Reset(properties); + } + + // This is the only method that absolutely must be overridden, + // because without it the KeyedCollection cannot extract the + // keys from the items. The input parameter type is the + // second generic type argument, in this case OrderItem, and + // the return value type is the first generic type argument, + // in this case int. + // + protected override int GetKeyForItem(OrderItem item) + { + // In this example, the key is the part number. + return item.PartNumber; + } + + internal void Reset(IEnumerable properties) + { + Clear(); + properties.ForEach(Add); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + protected override void SetItem(int index, OrderItem item) + { + base.SetItem(index, item); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); + } + + protected override void RemoveItem(int index) + { + var removed = this[index]; + base.RemoveItem(index); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed)); + } + + protected override void InsertItem(int index, OrderItem item) + { + base.InsertItem(index, item); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); + } + + protected override void ClearItems() + { + base.ClearItems(); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + public new bool Contains(int partNumber) + { + return this.Any(x => x.PartNumber == partNumber); + } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args) + { + if (CollectionChanged != null) + { + CollectionChanged(this, args); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/ContentTypeTests.cs b/src/Umbraco.Tests/Models/ContentTypeTests.cs index 71f03614e1..9f2e97bbc9 100644 --- a/src/Umbraco.Tests/Models/ContentTypeTests.cs +++ b/src/Umbraco.Tests/Models/ContentTypeTests.cs @@ -10,6 +10,25 @@ namespace Umbraco.Tests.Models [TestFixture] public class ContentTypeTests { + [Test] + public void Can_Deep_Clone_Content_Type_Sort() + { + var contentType = new ContentTypeSort(new Lazy(() => 3), 4, "test"); + var clone = (ContentTypeSort) contentType.DeepClone(); + Assert.AreNotSame(clone, contentType); + Assert.AreEqual(clone, contentType); + Assert.AreEqual(clone.Id.Value, contentType.Id.Value); + Assert.AreEqual(clone.SortOrder, contentType.SortOrder); + Assert.AreEqual(clone.Alias, contentType.Alias); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(contentType, null)); + } + } + [Test] public void Can_Deep_Clone_Content_Type() { diff --git a/src/Umbraco.Tests/Models/DataTypeDefinitionTests.cs b/src/Umbraco.Tests/Models/DataTypeDefinitionTests.cs new file mode 100644 index 0000000000..e731b3610d --- /dev/null +++ b/src/Umbraco.Tests/Models/DataTypeDefinitionTests.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class DataTypeDefinitionTests + { + [Test] + public void Can_Deep_Clone() + { + var dtd = new DataTypeDefinition(9, Guid.NewGuid()) + { + CreateDate = DateTime.Now, + CreatorId = 5, + DatabaseType = DataTypeDatabaseType.Nvarchar, + Id = 4, + Key = Guid.NewGuid(), + Level = 7, + Name = "Test", + ParentId = 9, + Path = "-1,2", + SortOrder = 8, + Trashed = true, + UpdateDate = DateTime.Now + }; + var clone = (DataTypeDefinition) dtd.DeepClone(); + + Assert.AreNotSame(clone, dtd); + Assert.AreEqual(clone, dtd); + Assert.AreEqual(clone.CreateDate, dtd.CreateDate); + Assert.AreEqual(clone.CreatorId, dtd.CreatorId); + Assert.AreEqual(clone.DatabaseType, dtd.DatabaseType); + Assert.AreEqual(clone.Id, dtd.Id); + Assert.AreEqual(clone.Key, dtd.Key); + Assert.AreEqual(clone.Level, dtd.Level); + Assert.AreEqual(clone.Name, dtd.Name); + Assert.AreEqual(clone.ParentId, dtd.ParentId); + Assert.AreEqual(clone.Path, dtd.Path); + Assert.AreEqual(clone.SortOrder, dtd.SortOrder); + Assert.AreEqual(clone.Trashed, dtd.Trashed); + Assert.AreEqual(clone.UpdateDate, dtd.UpdateDate); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(dtd, null)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/DictionaryItemTests.cs b/src/Umbraco.Tests/Models/DictionaryItemTests.cs new file mode 100644 index 0000000000..32fa2f127e --- /dev/null +++ b/src/Umbraco.Tests/Models/DictionaryItemTests.cs @@ -0,0 +1,82 @@ +using System; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class DictionaryItemTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new DictionaryItem("blah") + { + CreateDate = DateTime.Now, + Id = 8, + ItemKey = "blah", + Key = Guid.NewGuid(), + ParentId = Guid.NewGuid(), + UpdateDate = DateTime.Now, + Translations = new[] + { + new DictionaryTranslation(new Language("en-AU") + { + CreateDate = DateTime.Now, + CultureName = "en", + Id = 11, + IsoCode = "AU", + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }, "colour") + { + CreateDate = DateTime.Now, + Id = 88, + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }, + new DictionaryTranslation(new Language("en-US") + { + CreateDate = DateTime.Now, + CultureName = "en", + Id = 12, + IsoCode = "US", + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }, "color") + { + CreateDate = DateTime.Now, + Id = 89, + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }, + } + }; + + var clone = (DictionaryItem)item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.ItemKey, item.ItemKey); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.ParentId, item.ParentId); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + Assert.AreEqual(clone.Translations.Count(), item.Translations.Count()); + for (var i = 0; i < item.Translations.Count(); i++) + { + Assert.AreNotSame(clone.Translations.ElementAt(i), item.Translations.ElementAt(i)); + Assert.AreEqual(clone.Translations.ElementAt(i), item.Translations.ElementAt(i)); + } + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/DictionaryTranslationTests.cs b/src/Umbraco.Tests/Models/DictionaryTranslationTests.cs new file mode 100644 index 0000000000..2c585989c4 --- /dev/null +++ b/src/Umbraco.Tests/Models/DictionaryTranslationTests.cs @@ -0,0 +1,49 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class DictionaryTranslationTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new DictionaryTranslation(new Language("en-AU") + { + CreateDate = DateTime.Now, + CultureName = "en", + Id = 11, + IsoCode = "AU", + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }, "colour") + { + CreateDate = DateTime.Now, + Id = 88, + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }; + + var clone = (DictionaryTranslation) item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + Assert.AreNotSame(clone.Language, item.Language); + Assert.AreEqual(clone.Language, item.Language); + Assert.AreEqual(clone.Value, item.Value); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/LanguageTests.cs b/src/Umbraco.Tests/Models/LanguageTests.cs new file mode 100644 index 0000000000..0637aa1b99 --- /dev/null +++ b/src/Umbraco.Tests/Models/LanguageTests.cs @@ -0,0 +1,41 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class LanguageTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new Language("en-AU") + { + CreateDate = DateTime.Now, + CultureName = "en", + Id = 11, + IsoCode = "AU", + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }; + + var clone = (Language) item.DeepClone(); + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.CultureName, item.CultureName); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.IsoCode, item.IsoCode); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/RelationTests.cs b/src/Umbraco.Tests/Models/RelationTests.cs new file mode 100644 index 0000000000..3293bd76cf --- /dev/null +++ b/src/Umbraco.Tests/Models/RelationTests.cs @@ -0,0 +1,48 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class RelationTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new Relation(9, 8, new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test") + { + Id = 66 + }) + { + Comment = "test comment", + CreateDate = DateTime.Now, + Id = 4, + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }; + + var clone = (Relation) item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.ChildId, item.ChildId); + Assert.AreEqual(clone.Comment, item.Comment); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.ParentId, item.ParentId); + Assert.AreNotSame(clone.RelationType, item.RelationType); + Assert.AreEqual(clone.RelationType, item.RelationType); + Assert.AreEqual(clone.RelationTypeId, item.RelationTypeId); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/RelationTypeTests.cs b/src/Umbraco.Tests/Models/RelationTypeTests.cs new file mode 100644 index 0000000000..69850bcb51 --- /dev/null +++ b/src/Umbraco.Tests/Models/RelationTypeTests.cs @@ -0,0 +1,44 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class RelationTypeTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test") + { + Id = 66, + CreateDate = DateTime.Now, + IsBidirectional = true, + Key = Guid.NewGuid(), + Name = "Test", + UpdateDate = DateTime.Now + }; + + var clone = (RelationType)item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.Alias, item.Alias); + Assert.AreEqual(clone.ChildObjectType, item.ChildObjectType); + Assert.AreEqual(clone.IsBidirectional, item.IsBidirectional); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.Name, item.Name); + Assert.AreNotSame(clone.ParentObjectType, item.ParentObjectType); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/TaskTests.cs b/src/Umbraco.Tests/Models/TaskTests.cs new file mode 100644 index 0000000000..8518697f24 --- /dev/null +++ b/src/Umbraco.Tests/Models/TaskTests.cs @@ -0,0 +1,52 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class TaskTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new Task(new TaskType("test") {Id = 3}) + { + AssigneeUserId = 4, + Closed = true, + Comment = "blah", + CreateDate = DateTime.Now, + EntityId = 99, + Id = 2, + Key = Guid.NewGuid(), + OwnerUserId = 89, + TaskType = new TaskType("asdf") {Id = 99}, + UpdateDate = DateTime.Now + }; + + var clone = (Task) item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + Assert.AreEqual(clone.AssigneeUserId, item.AssigneeUserId); + Assert.AreEqual(clone.Closed, item.Closed); + Assert.AreEqual(clone.Comment, item.Comment); + Assert.AreEqual(clone.EntityId, item.EntityId); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.OwnerUserId, item.OwnerUserId); + Assert.AreNotSame(clone.TaskType, item.TaskType); + Assert.AreEqual(clone.TaskType, item.TaskType); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/TaskTypeTests.cs b/src/Umbraco.Tests/Models/TaskTypeTests.cs new file mode 100644 index 0000000000..65f6a8ef34 --- /dev/null +++ b/src/Umbraco.Tests/Models/TaskTypeTests.cs @@ -0,0 +1,42 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class TaskTypeTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new TaskType("test") + { + Id = 3, + Alias = "test", + CreateDate = DateTime.Now, + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now + }; + + var clone = (TaskType)item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.Alias, item.Alias); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/TemplateTests.cs b/src/Umbraco.Tests/Models/TemplateTests.cs new file mode 100644 index 0000000000..12752a8efa --- /dev/null +++ b/src/Umbraco.Tests/Models/TemplateTests.cs @@ -0,0 +1,54 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class TemplateTests + { + [Test] + public void Can_Deep_Clone() + { + var item = new Template("-1,2,3", "Test", "test") + { + Id = 3, + CreateDate = DateTime.Now, + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now, + Content = "blah", + CreatorId = 66, + Level = 55, + ParentId = 2, + SortOrder = 99, + MasterTemplateAlias = "master", + MasterTemplateId = new Lazy(() => 88) + }; + + var clone = (Template)item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.Alias, item.Alias); + Assert.AreEqual(clone.CreatorId, item.CreatorId); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.Level, item.Level); + Assert.AreEqual(clone.MasterTemplateAlias, item.MasterTemplateAlias); + Assert.AreEqual(clone.MasterTemplateId.Value, item.MasterTemplateId.Value); + Assert.AreEqual(clone.Name, item.Name); + Assert.AreEqual(clone.ParentId, item.ParentId); + Assert.AreEqual(clone.SortOrder, item.SortOrder); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/UmbracoEntityTests.cs b/src/Umbraco.Tests/Models/UmbracoEntityTests.cs index 2be59c7b07..789dee709c 100644 --- a/src/Umbraco.Tests/Models/UmbracoEntityTests.cs +++ b/src/Umbraco.Tests/Models/UmbracoEntityTests.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using System; +using NUnit.Framework; using Umbraco.Core.Models; namespace Umbraco.Tests.Models @@ -18,5 +19,81 @@ namespace Umbraco.Tests.Models Assert.IsTrue(trashedWithBool.Trashed); Assert.IsTrue(trashedWithInt.Trashed); } + + [Test] + public void Can_Deep_Clone() + { + var item = new UmbracoEntity() + { + Id = 3, + ContentTypeAlias = "test1", + CreatorId = 4, + Key = Guid.NewGuid(), + UpdateDate = DateTime.Now, + CreateDate = DateTime.Now, + Name = "Test", + ParentId = 5, + SortOrder = 6, + Path = "-1,23", + Level = 7, + ContentTypeIcon = "icon", + ContentTypeThumbnail = "thumb", + HasChildren = true, + HasPendingChanges = true, + IsDraft = true, + IsPublished = true, + NodeObjectTypeId = Guid.NewGuid() + }; + item.AdditionalData.Add("test1", 3); + item.AdditionalData.Add("test2", "valuie"); + item.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty() + { + Value = "test", + DataTypeControlId = Guid.NewGuid() + }); + item.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty() + { + Value = "test2", + DataTypeControlId = Guid.NewGuid() + }); + + var clone = (UmbracoEntity)item.DeepClone(); + + Assert.AreNotSame(clone, item); + Assert.AreEqual(clone, item); + Assert.AreEqual(clone.CreateDate, item.CreateDate); + Assert.AreEqual(clone.ContentTypeAlias, item.ContentTypeAlias); + Assert.AreEqual(clone.CreatorId, item.CreatorId); + Assert.AreEqual(clone.Id, item.Id); + Assert.AreEqual(clone.Key, item.Key); + Assert.AreEqual(clone.Level, item.Level); + Assert.AreEqual(clone.Name, item.Name); + Assert.AreEqual(clone.ParentId, item.ParentId); + Assert.AreEqual(clone.SortOrder, item.SortOrder); + Assert.AreEqual(clone.Path, item.Path); + Assert.AreEqual(clone.ContentTypeIcon, item.ContentTypeIcon); + Assert.AreEqual(clone.ContentTypeThumbnail, item.ContentTypeThumbnail); + Assert.AreEqual(clone.HasChildren, item.HasChildren); + Assert.AreEqual(clone.HasPendingChanges, item.HasPendingChanges); + Assert.AreEqual(clone.IsDraft, item.IsDraft); + Assert.AreEqual(clone.IsPublished, item.IsPublished); + Assert.AreEqual(clone.NodeObjectTypeId, item.NodeObjectTypeId); + Assert.AreEqual(clone.UpdateDate, item.UpdateDate); + Assert.AreEqual(clone.AdditionalData.Count, item.AdditionalData.Count); + Assert.AreEqual(clone.AdditionalData, item.AdditionalData); + Assert.AreEqual(clone.UmbracoProperties.Count, item.UmbracoProperties.Count); + for (var i = 0; i < clone.UmbracoProperties.Count; i++) + { + Assert.AreNotSame(clone.UmbracoProperties[i], item.UmbracoProperties[i]); + Assert.AreEqual(clone.UmbracoProperties[i], item.UmbracoProperties[i]); + } + + //This double verifies by reflection + var allProps = clone.GetType().GetProperties(); + foreach (var propertyInfo in allProps) + { + Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null)); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 14dc64f41a..38e41ffe68 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -157,9 +157,21 @@ + + + + + + + + + + + +